在C#应用程序中,日志系统是排查问题、监控运行状态的基础手段。通过微软官方的Logging抽象库,我们可以在不需要引入第三方框架的情况下,灵活控制不同类别、不同严重程度的日志是否输出、输出到哪里。理解日志级别与过滤机制,是写好可维护日志的第一步。

一、C#日志级别(LogLevel)概述
C#中的日志级别由Microsoft.Extensions.Logging.LogLevel枚举定义,从最低到最高依次为:Trace、Debug、Information、Warning、Error、Critical,以及一个特殊的None。级别数值越大,代表消息越严重。Trace用于最详细的跟踪信息,Debug用于开发期调试,Information是常规业务记录,Warning表示可能出现的问题,Error是已发生的错误,Critical则是导致程序无法继续的严重故障。
在实际编码时,我们通过ILogger实例调用对应方法,例如LogInformation或LogError。如果当前配置的最小记录级别高于调用方法的级别,这条日志就不会被任何提供器处理。这种基于级别的设计,让我们可以在不同环境(开发、测试、生产)使用同一套日志代码,仅修改配置即可调整详细程度。
1.1 各级别适用场景
Trace和Debug通常只在本地开发开启。比如循环中的变量值、方法进出记录,用Trace最合适;而接口参数校验失败但可恢复的情况,用Debug更贴切。Information适合记录用户登录、订单创建等具有业务意义的事件。Warning常用于下游服务响应慢、重试次数较多但暂未失败的情形。Error和Critical则必须接入告警系统,前者如数据库写入异常,后者如进程启动所需配置文件丢失。
很多初学者会把所有信息都写成Information,导致生产环境日志暴涨。合理的做法是:先假设生产只开Warning,再把真正需要长期观察的业务流转补充为Information,调试细节留在Debug。这样分类后,日志量能下降一个数量级。
二、基础Logging配置方式
在.NET中,日志系统由ILoggerFactory创建,并通过添加提供器(Provider)决定输出目标。最基础的配置是在代码中手动构建,也可以使用appsettings.json配合依赖注入。下面先看一个纯代码的基础示例,使用控制台提供器:
using Microsoft.Extensions.Logging;
// 创建日志工厂,添加控制台提供器
using ILoggerFactory factory = LoggerFactory.Create(builder =>
{
builder.AddConsole();
// 设置默认最小级别为 Information
builder.SetMinimumLevel(LogLevel.Information);
});
ILogger<Program> logger = factory.CreateLogger<Program>();
logger.LogDebug("这是调试信息,不会显示");
logger.LogInformation("服务已启动");
logger.LogWarning("配置项缺失,使用默认值");
上述代码中,SetMinimumLevel设定了全局最低级别。因为设为Information,所以LogDebug不会被输出。这种写法适合简单工具或示例,但在真实项目中,我们更推荐用配置文件解耦。
2.1 使用appsettings.json配置
借助AddConfiguration扩展,可以把级别和过滤写在JSON里,修改无需重新编译。典型结构如下:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
},
"Console": {
"LogLevel": {
"Default": "Information"
}
}
}
}
这段配置表示:默认类别记录Information及以上;以Microsoft开头的类别只记录Warning及以上,避免框架自身日志刷屏;控制台提供器单独沿用默认级别。在Program.cs中通过builder.Logging.AddConfiguration(config.GetSection("Logging"))即可载入。这种分类过滤思路,是控制日志噪声的核心。
三、日志过滤的规则与类别
日志类别(Category)通常是调用CreateLogger<T>时的类名全名,也可自定义字符串。过滤规则按“提供器名称 + 类别前缀”匹配,最长前缀优先。例如规则Microsoft.AspNetCore比Microsoft更具体,会覆盖后者。
过滤不仅支持全局默认,还能针对单个提供器。假设我们同时挂了控制台和文件,想让文件记Debug、控制台只记Warning,就可以分别在Logging:Console:LogLevel和Logging:File:LogLevel里写不同值。下面用代码演示多提供器下的分别过滤:
using Microsoft.Extensions.Logging;
using ILoggerFactory factory = LoggerFactory.Create(builder =>
{
builder.AddConsole(config =>
{
config.LogLevelSwitches["Default"] = LogLevel.Warning;
});
builder.AddFile("logs/app.log", configure =>
{
configure.LogLevelSwitches["Default"] = LogLevel.Debug;
});
// 全局默认
builder.SetMinimumLevel(LogLevel.Debug);
});
var logger = factory.CreateLogger("OrderService");
logger.LogDebug("调试:订单校验通过");
logger.LogWarning("警告:库存同步延迟");
注意上面伪代码里的AddFile在某些SDK需第三方包,但配置逻辑一致:提供器内部开关优先于全局。通过类别命名约定,如OrderService、UserService,我们可以在配置里对业务模块单独降级或提级,而不动代码。
3.1 过滤顺序与陷阱
匹配时,系统先找“提供器+具体类别”规则,再找“提供器+Default”,然后“全局+类别”,最后“全局Default”。若写了Default:Debug却又发现Error没出来,多半是某条更具体的规则把级别抬到了Critical。排查时建议打印出有效配置,或暂时把所有具体规则删掉只看全局。
另一个常见误区是以为LogLevel过滤会“丢弃内容但保留计数”。实际上低于级别的日志在ILogger调用时就被IsEnabled短路,不会产生任何开销,因此放心用细粒度级别写调试日志即可,不影响生产性能。
四、按类别分流的实践示例
在稍大的系统里,我们常希望支付相关日志单独存文件,普通业务走控制台。利用类别前缀即可实现。以下示例定义两个Logger,分别写不同类别:
using Microsoft.Extensions.Logging;
using ILoggerFactory factory = LoggerFactory.Create(builder =>
{
builder.AddConsole();
builder.SetMinimumLevel(LogLevel.Information);
});
var payLogger = factory.CreateLogger("Payment");
var bizLogger = factory.CreateLogger("Business");
payLogger.LogInformation("支付网关返回码:200");
bizLogger.LogInformation("用户资料更新成功");
配合配置文件中Logging:Console:LogLevel:Payment=Error,就能让支付细节只在出错时浮现,平时保持安静。这种“代码打点、配置控量”的模式,是C#基础Logging最实用的架构。
总结来看,C#自带的日志体系虽轻量,却通过LogLevel与分层过滤提供了足够弹性。掌握级别含义、配置加载顺序以及类别命名规范,就能在低门槛下写出清晰、可控的日志方案,为后续接入Serilog等高级框架打下稳固基础。
C#_Logging日志级别日志过滤修改时间:2026-08-01 03:18:34