serilog必须显式配置sink(如console/file)且asp.net core中需通过useserilog()接入di容器,否则ilogger注入失败;日志按controller分离应利用sourcecontext+conditional,滚动策略推荐rollinginterval.day。

为什么直接 new LoggerConfiguration().CreateLogger() 会报错
Serilog 的 Logger 是单例设计,但 LoggerConfiguration.CreateLogger() 返回的是一个可被多次使用的 ILogger 实例——它本身不报错;真正常见报错是「未配置任何 sink,调用 Log.Information() 时静默失败」或「在 ASP.NET Core 外部手动创建后,跨线程/作用域使用导致 NullReferenceException」。
根本原因:Serilog 默认不输出日志,必须显式添加 sink(如控制台、文件);且若没通过依赖注入注册,手动创建的 logger 不参与生命周期管理,容易在对象销毁后仍被调用。
- 务必至少调用一次
WriteTo.Console()或WriteTo.File() - 非 ASP.NET Core 环境下,建议将
ILogger存为静态字段或全局变量,避免反复创建 - ASP.NET Core 项目中,绝不要在 Startup / Program 中只调用
CreateLogger()就完事——要走UseSerilog()扩展方法接入 DI 容器
ASP.NET Core 6+ 中正确注册 Serilog 的三步写法
.NET 6 起 Program.cs 是隐式主函数,Serilog 必须在 WebApplication 创建前完成初始化,否则中间件、控制器里的 ILogger<t></t> 注入会失败。
using Serilog;
using Serilog.Events;
var builder = WebApplication.CreateBuilder(args);
// 1. 配置 Serilog(必须在 builder.Build() 前)
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.Enrich.FromLogContext()
.WriteTo.Console()
.WriteTo.File("logs/log-.txt", rollingInterval: RollingInterval.Day)
.CreateLogger();
// 2. 将 Serilog 接入 DI 容器(关键!)
builder.Host.UseSerilog(); // ← 这行不能漏
// 3. 后续正常注册服务
builder.Services.AddControllers();
var app = builder.Build();
app.MapControllers();
app.Run();
-
builder.Host.UseSerilog()是核心,它把 Serilog 绑定到Microsoft.Extensions.Logging.ILogger抽象层 - 如果跳过这步,Controller 构造函数注入的
ILogger<weatherforecastcontroller></weatherforecastcontroller>仍是 Microsoft 默认 logger,和 Serilog 无关 -
Log.Logger全局静态实例仅用于提前记录启动日志(比如配置加载失败),业务代码应始终依赖 DI 注入的ILogger<t></t>
如何让不同 Controller 输出不同格式的日志
Serilog 本身不支持按类型动态切换输出格式,但可通过 Enrich.WithProperty() + 自定义 sink 过滤,或更实际的做法:用 SourceContext 字段做区分,再配合 WriteTo.Conditional() 控制输出目标。
最轻量可控的方式是利用 ILogger<t></t> 的泛型类型名自动注入 SourceContext,然后在 sink 中过滤:
Log.Logger = new LoggerConfiguration()
.WriteTo.Conditional(
evt => evt.Properties.ContainsKey("SourceContext") &&
evt.Properties["SourceContext"].ToString().Contains("AdminController"),
wt => wt.File("logs/admin-.log", rollingInterval: RollingInterval.Day))
.WriteTo.Conditional(
evt => evt.Properties.ContainsKey("SourceContext") &&
evt.Properties["SourceContext"].ToString().Contains("UserController"),
wt => wt.File("logs/user-.log", rollingInterval: RollingInterval.Day))
.CreateLogger();
-
SourceContext是 Serilog 自动写入的字段,值就是typeof(T).FullName,无需手动设置 - 条件判断必须用
evt.Properties.ContainsKey()防空,否则日志事件可能无SourceContext(如直接调Log.Information()) - 不推荐为每个 Controller 新建独立
ILogger实例——破坏 DI 原则,也难统一管理日志级别
文件日志滚动策略选 RollingInterval.Day 还是 RollingInterval.Infinite
RollingInterval.Day 每天生成新文件(如 log-20240520.txt),适合需要按日期归档、配合运维脚本清理的场景;RollingInterval.Infinite 则永不切分,只靠 retainedFileCountLimit 控制保留几个历史文件——但它其实不会“无限追加”,而是达到上限后删除最旧文件。
- 生产环境强烈建议用
RollingInterval.Day或RollingInterval.Hour,避免单文件过大导致打开/传输/分析困难 - 若选
RollingInterval.Infinite,必须配retainedFileCountLimit: 30,否则磁盘会被撑爆 - 注意 Windows 下文件名中的冒号
:不合法,所以RollingInterval.Hour生成的文件名是log-20240520_14.txt(用下划线替代冒号)
日志路径里带中文或空格一般没问题,但绝对路径开头别写 ~\——Serilog 的 WriteTo.File() 不解析 tilde,要用 AppContext.BaseDirectory 拼接。










