CodeGym /课程 /C# SELF /日志记录: Microsoft.Extensions...

日志记录: Microsoft.Extensions.Logging

C# SELF
第 64 级, 课程 0
可用

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 时会用到的关键对象:

类/接口 用途
ILogger<T>
按类类型化的日志接口
ILogger
通用的日志接口
ILoggerFactory
创建 logger 实例的工厂
ILoggingBuilder
用来配置应用中的日志
LogLevel
日志级别枚举(Trace, Debug, Information, ...)

日志级别

按重要性把消息分类可以过滤噪声、快速定位:

级别 (LogLevel) 什么时候用
Trace
最详细的调试,噪声级别,临时数据
Debug
主要的调试信息
Information
正常运行的关键事件消息
Warning
潜在问题的警告,但系统仍在运行
Error
需要关注的错误,但应用还活着
Critical
危及整个系统的严重故障

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)会制造噪声,而缺乏日志又会让诊断变难。

2
任务
C# SELF, 第 64 级, 课程 0
已锁定
使用 'Microsoft.Extensions.Logging' 进行基础日志记录
使用 'Microsoft.Extensions.Logging' 进行基础日志记录
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION