必须调用builder.host.useserilog()将serilog挂入di生命周期,否则ilogger注入的是默认日志器;log.logger静态初始化与di无关;writeto至少需一个sink且路径含-或.才滚动;结构化日志需严格匹配字段名、大小写及{@}语法。

直接在 Program.cs 顶部初始化 Log.Logger,并调用 builder.Host.UseSerilog(),否则 ILogger<t></t> 注入的永远是 Microsoft 默认日志器,不是 Serilog。
为什么 ILogger 注入的是空壳?
根本原因不是配置写错了,而是 Serilog 没被挂进 DI 生命周期。ASP.NET Core 启动时会先创建自己的 ILoggerFactory,如果你没显式调用 UseSerilog(),它就完全不感知 Serilog 的存在。
-
Log.Logger = new LoggerConfiguration().CreateLogger()只是建了个静态实例,和 DI 容器无关 -
builder.Services.AddLogging(...)手动注册也无效——必须走builder.Host.UseSerilog() - 常见现象:控制器里
ILogger<weathercontroller></weathercontroller>能注入成功,但Log.Information()写不到文件、控制台没输出、LogContext.PushProperty()完全不生效
WriteTo 配置后日志仍不落地?
Serilog 默认静默失败:没配 sink 就不输出,也不报错。哪怕你写了 Log.Information("test"),路径权限不足、磁盘满、目录不存在,它都一声不吭。
- 必须至少配置一个 sink:
.WriteTo.Console()或.WriteTo.File("logs/app-.log") - 文件路径中必须含
-或.才触发滚动(如"logs/app-.log"→app-20260514.log;而"logs/app.log"永远只写一个文件) - Windows 下路径权限问题高发:确保应用对
logs/目录有写权限,否则.WriteTo.File()直接失效
结构化日志为什么查不到字段?
不是“写了占位符就自动结构化”,而是字段名、大小写、参数顺序三者必须严丝合缝,且 sink 必须支持结构化传输。
- 错误写法:
Log.Information($"User {id} logged in")→ 输出纯文本,无Properties字段 - 正确写法:
Log.Information("User {UserId} logged in", id)→UserId是字段名,区分大小写 - 传对象必须用
{@User}(带@),否则只调.ToString(),序列化不出属性 - 推送到 Seq/Elasticsearch 时,若没装对应 sink(如
Serilog.Sinks.Seq)或没配地址/ApiKey,日志会丢得毫无痕迹
appsettings.json 配置不生效?
Serilog.Settings.Configuration 不是自动加载,它只读 "Serilog" 节点,且必须显式调用 .ReadFrom.Configuration()。
- 缺 NuGet 包:
Serilog.Settings.Configuration必须安装 - 缺调用:
.ReadFrom.Configuration(builder.Configuration)必须加在LoggerConfiguration链中 - JSON 结构要完整:
"Serilog"是顶层键,不能嵌在"Logging"下;"WriteTo"是数组,每个项必须有"Name" - 示例有效片段:
{ "Serilog": { "MinimumLevel": "Debug", "WriteTo": [ { "Name": "Console" }, { "Name": "File", "Args": { "path": "logs/app-.log", "rollingInterval": "Day" } } ] } }
最常被忽略的一点:日志级别过滤和 sink 级别控制是两回事。全局 MinimumLevel.Debug() 只决定“哪些日志事件被创建”,而 restrictedToMinimumLevel: Warning 才决定“该 sink 是否接收这条日志”。不设这个,控制台刷满 Debug,文件却漏掉关键上下文——这不是 bug,是设计如此。










