为何var声明的Serilog Logger无输出?必须用Log.logger静态实例吗?
问题描述
我在.NET环境中编写了如下Serilog配置代码:
var logger = new LoggerConfiguration() .Enrich.WithProperty("Assembly", assembly?.Name) .Enrich.FromLogContext() .MinimumLevel.Debug() .MinimumLevel.Override("Microsoft", LogEventLevel.Debug) .WriteTo.Console( theme: Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme.Code, formatProvider: CultureInfo.CurrentCulture, outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff}][{Level:u3}] {Message:lj} <s:{SourceContext}>{NewLine}{Exception}", restrictedToMinimumLevel: LogEventLevel.Debug) .WriteTo.File( path: "Logs/log-.log", outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff}][{Level:u3}] {Message:lj} <s:{SourceContext}>{NewLine}{Exception}", rollingInterval: RollingInterval.Day, restrictedToMinimumLevel: LogEventLevel.Debug, formatProvider: CultureInfo.CurrentCulture) .WriteTo.Sink(new WebSocketSink(logChannel), LogEventLevel.Debug) .WriteTo.Seq("http://localhost:5341") .CreateLogger();
将该logger实例注入到依赖ILogger<MyService>的MyService类时,日志语句既不输出到控制台也不写入文件:
public class MyService(IDeviceIO devIO, ILogger<MyService> logger) : IMyService { /// <inheritdoc/> public void OpenDevice(string host) { logger.LogInformation("Device is being opened!"); devIO.Open(host); } // ... }
但改成Log.Logger = new LoggerConfiguration()...后,日志开始正常输出到控制台和文件。请问为何用var logger = ...声明的实例不行?必须始终使用Log.Logger静态实例吗?
原因分析
核心问题是你仅创建了Serilog的Logger实例,但没有将其注册到.NET的依赖注入容器中,也未让.NET的日志系统桥接至该实例。
当你通过ILogger<MyService>注入时,默认获取的是.NET原生LoggerProvider提供的实例,而非你自行创建的Serilog实例。只有将Serilog配置为.NET日志系统的底层实现后,注入的ILogger<T>才会委托Serilog处理日志输出。
而Log.Logger是Serilog的全局静态日志实例,设置它之后,Serilog与.NET日志系统的桥接组件(如Serilog.Extensions.Logging)会默认使用该全局实例,因此日志能正常输出,但这并非最优方案——全局静态实例不利于测试和依赖隔离。
正确解决方式
无需依赖Log.Logger静态实例,只需将你创建的Serilog实例注册到DI容器,并替换.NET默认日志系统即可,步骤如下:
- 确保已安装
Serilog.Extensions.Logging包(这是Serilog与.NET日志系统的桥接包) - 在DI配置代码中(例如Program.cs),将Serilog实例注册为日志提供者:
// 创建自定义配置的Serilog实例 var logger = new LoggerConfiguration() // 此处保留你的原有配置代码 .CreateLogger(); // 注册到DI容器,替换默认日志系统 builder.Services.AddLogging(loggingBuilder => { // 清除所有默认的日志提供者 loggingBuilder.ClearProviders(); // 添加Serilog作为唯一的日志提供者 loggingBuilder.AddSerilog(logger); });
完成上述配置后,注入ILogger<MyService>时,获取到的就是绑定了你自定义配置的Serilog实例的日志对象,日志会正常输出到你指定的控制台、文件等目标。
另外需要注意:创建的Serilog实例需保持生命周期,避免被GC回收——不过通过AddSerilog注册到DI容器后,容器会自动管理其生命周期,无需额外担心。
内容的提问来源于stack exchange,提问作者BND

