1. 介绍
在编程里,日志记录不只是“往控制台输出点东西”。日志是你的黑匣子、GPS 追踪器和指示灯合在一起的工具。没有日志,大型程序就变成了一个谜一样的黑匣子:“为什么服务卡住了?”,“为什么用户没收到邮件?”,“这个模块最近三个月有人运行过吗?”——这些问题通常只有在日志被保存并可访问时才能回答。
现实中为什么需要日志?
查错和诊断
如果程序崩溃了——日志会告诉你在哪里和为什么。如果没崩但行为怪——日志会指出导致问题的步骤。
监控状态
通过日志可以知道系统现在是否工作、服务器请求量多不多、有无错误、谁在做什么。
安全
记录未授权访问尝试、可疑活动、认证错误。
审计
谁在什么时候做了什么。如果某个不怀好意的管理员离职并删除了数据,通过日志可以恢复(或至少知道他做了什么)。
开发与运维支持
日志不仅给程序员看,也给测试人员、运维和管理员看。过半年你会感谢自己: '谢谢你当初加了日志!'
从 Console.WriteLine 到企业级日志
新手的第一反应:“就用 Console.WriteLine 不行吗?”。对于小型练习程序确实够用。但当应用部署到服务器、并发运行,或者有成百上千用户时,控制台就不够用了。我们需要:
- 把重要信息和调试信息区分开。
- 把日志不仅写到控制台,还写到文件、数据库、集中式系统。
- 能改变日志的详细程度(最少——只有错误,最多——全部记录)。
- 自动给条目补上日期、时间和额外信息。
- 灵活配置。
- 最重要的——不想重新改代码就能把日志重定向到别的地方。
这就是“真正”日志库开始发挥作用的地方。
2. .NET 现代日志系统基础
在 .NET 生态里有一个标准、强大且灵活的日志框架——Microsoft.Extensions.Logging。它是随着 ASP.NET Core 出现的新一代库的一部分,但现在处处可用:服务器、桌面,甚至移动应用都能用。
这个框架的优点
- 抽象化——不把你锁死在某个实现上(logger 可以写文件、控制台、云端,或者同时写到多个地方)。
- 支持日志级别(Trace, Debug, Information, Warning, Error, Critical)。
- 可与 DI 容器集成,适配所有现代 .NET 应用。
- 丰富的扩展生态:支持结构化日志、高级格式化器和各种整合。
主要概念和类
我们来认识几个在使用 Microsoft.Extensions.Logging 时会用到的关键对象:
| 类/接口 | 用途 |
|---|---|
|
按类类型化的日志接口 |
|
通用的日志接口 |
|
创建 logger 实例的工厂 |
|
用来配置应用中的日志 |
|
日志级别枚举(Trace, Debug, Information, ...) |
日志级别
按重要性把消息分类可以过滤噪声、快速定位:
| 级别 (LogLevel) | 什么时候用 |
|---|---|
|
最详细的调试,噪声级别,临时数据 |
|
主要的调试信息 |
|
正常运行的关键事件消息 |
|
潜在问题的警告,但系统仍在运行 |
|
需要关注的错误,但应用还活着 |
|
危及整个系统的严重故障 |
3. 实践:在应用中加入日志
别写抽象代码,我们继续扩展示例应用。假设已有一个简单的 Calculator 类,课程进度中我们会逐步扩展它。
示例:基础计算器
public class Calculator
{
public int Add(int a, int b)
{
return a + b;
}
// 其他方法...
}
现在给它加上日志。为此我们需要接口 ILogger<Calculator>,这个接口我们会从外面传入(比如通过依赖注入 DI)。
using Microsoft.Extensions.Logging;
public class Calculator
{
private readonly ILogger<Calculator> _logger;
public Calculator(ILogger<Calculator> logger)
{
_logger = logger;
}
public int Add(int a, int b)
{
int result = a + b;
_logger.LogInformation("已执行加法: {A} + {B} = {Result}", a, b, result);
return result;
}
}
小知识:
logger 支持模板和命名参数({A}, {B}, {Result}),这能让日志变成结构化的,便于后续自动处理和搜索。
4. 如何在控制台应用中创建和配置 logger
1. 引入 NuGet 包
项目里需要:
- Microsoft.Extensions.Logging
- Microsoft.Extensions.Logging.Console(如果想写到控制台)
- (可选)需要时再添加其他提供器
2. 配置 logger
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
// 创建 DI 容器
var serviceProvider = new ServiceCollection()
.AddLogging(builder => {
builder.AddConsole(); // 输出到控制台
builder.SetMinimumLevel(LogLevel.Debug); // 最低日志级别
})
.BuildServiceProvider();
// 获取指定类型的 logger 实例
var logger = serviceProvider.GetRequiredService<ILogger<Calculator>>();
var calculator = new Calculator(logger);
calculator.Add(5, 3); // 这会产生日志消息
新手常见错误
“为什么控制台没看到日志?”——检查是否添加了对应的提供器(.AddConsole())以及最低级别(SetMinimumLevel)设置是否正确。如果级别设置得更高,低级别消息会被“过滤”掉!
5. 有用的细节
记录错误和异常情况
比如我们想记录除以零。加上对应的方法:
public int Divide(int a, int b)
{
if (b == 0)
{
_logger.LogError("尝试除以零! a={A}", a);
throw new DivideByZeroException();
}
int result = a / b;
_logger.LogInformation("已执行除法: {A} / {B} = {Result}", a, b, result);
return result;
}
为什么要这么做?
在真实应用中,当出问题时,Error 级别的日志通常会被特别关注:它们会自动发给管理员、在监控中被高亮并用于告警/通知。
使用类别和范围(scopes)
.NET 的 logger 支持所谓的 scopes——这是在一定代码块内自动添加到所有日志的额外元数据。例如处理一个 web 请求或用户会话时,可以把它的 id 放到 scope 里。
using (_logger.BeginScope("UserId: {UserId}", 42))
{
_logger.LogInformation("开始处理用户数据");
// ...
}
块内的所有消息都会带上额外标签 UserId: 42,以后便于按用户或操作查找日志。
示例:日志级别演示
_logger.LogTrace("这是 Trace —— 几乎没人会看到");
_logger.LogDebug("这是 Debug —— 给开发者看的");
_logger.LogInformation("这是 Information —— 正常运行事件");
_logger.LogWarning("这是 Warning —— 可能的问题警告");
_logger.LogError("这是 Error —— 需要关注的错误");
_logger.LogCritical("这是 Critical —— 系统着火了,需要消防员!");
如果你设置了 SetMinimumLevel(LogLevel.Information),你只会看到级别为 Information, Warning, Error, Critical 的消息。
小技巧:
把 Trace 和 Debug 留给开发阶段做详细诊断,生产环境通常只开到 Information 及以上,这样既不会让日志膨胀,也不会在噪声里漏掉重要信息。
可视化示意:现代日志架构
graph TD
A[应用代码] --ILogger<YourClass>--> B[Microsoft.Extensions.Logging]
B --> C1[Console Provider]
B --> C2[File Provider]
B --> C3[Cloud/Database Provider]
C1 -.-> D1[控制台中的日志]
C2 -.-> D2[文件中的日志]
C3 -.-> D3[监控系统中的日志]
subgraph Providers
C1
C2
C3
end
6. 额外功能和扩展
结构化日志:
参数值不仅可以藏在字符串里,也可以以键值对形式保存,这样就能按这些参数搜集和聚合(比如通过 Seq、ELK/ElasticSearch 或 Application Insights)。
日志提供器:
可以添加很多不同的提供器:文件、Windows EventLog、Azure 等等。
通过 appsettings.json 配置日志
在 ASP.NET Core 中,日志可以通过配置文件灵活配置,无需重新编译应用。
通过配置设置最小日志级别的示例(appsettings.json):
{
"Logging": {
"LogLevel": {
"Default": "Information",
"MyApp.Calculator": "Debug",
"Microsoft": "Warning"
}
}
}
7. 使用 Microsoft.Extensions.Logging 时的常见错误
错误 №1:错误选择日志级别。
把错误用 LogInformation 记录,而不是用 LogError 或 LogCritical,会让在生产环境中查问题变得困难。
错误 №2:忽视结构化日志。
用字符串拼接替代带占位符的模板({Parameter})会丧失结构化日志的优势,比如基于参数的搜索。
错误 №3:错误配置日志级别。
如果最低级别设置得比需要的高(比如设置为 Warning 而不是 Debug),重要消息会被过滤掉。
错误 №4:日志过多或过少。
过于详细的日志(例如在生产开启 Trace)会制造噪声,而缺乏日志又会让诊断变难。
GO TO FULL VERSION