Serilog 实战指南:.NET 结构化日志的事实标准

Serilog 中文实战指南:LoggerConfiguration 与六个级别、消息模板与 @ 解构、Sink 生态(Console/File/滚动文件)、Enricher 上下文增强、JSON 输出、ASP.NET Core 集成(UseSerilog)、Log.CloseAndFlush,以及观测云 DataKit 采集落地方案。

最佳实践
Serilog 实战指南:.NET 结构化日志的事实标准技术指南封面

Serilog 是 .NET 生态结构化日志的开创者和事实标准:消息模板、数百种 Sink 输出、一流的 JSON 支持。本文覆盖 Serilog 的核心配置、结构化字段、ASP.NET Core 集成与生产落地,并给出观测云平台接入方案。

核心要点速览

  • 消息模板是 Serilog 的灵魂Log.Information("用户 {UserId} 登录", id){UserId} 成为可查询的结构化字段。
  • Sink 决定日志去哪:Console、File(含滚动)、异步……数百种社区 Sink 覆盖几乎所有目的地。
  • Enricher 自动附加上下文:机器名、进程 ID、线程 ID 等统一注入,不用手写。
  • 生产落地:JSON 写 stdout,观测云 DataKit 采集,监控器告警,APM 关联。

快速上手:LoggerConfiguration

using Serilog;

using var log = new LoggerConfiguration()
    .MinimumLevel.Debug()                 // 最低级别 Debug
    .WriteTo.Console()                    // 输出到控制台
    .WriteTo.File("logs/app.log")         // 同时写文件
    .CreateLogger();

log.Verbose("Verbose message");   // 不输出(低于 Debug)
log.Debug("Debug message");
log.Information("应用启动");
log.Warning("配置缺失,使用默认值");
log.Error("数据库连接失败");
log.Fatal("系统级故障,即将退出");

六个级别:Verbose → Debug → Information(默认最低)→ Warning → Error → Fatal。控制台输出形如 [14:50:22 INF] 应用启动

消息模板与结构化字段

log.Information("用户 {UserId} 从 {Ip} 登录", user.Id, request.Ip);
// 解构复杂对象:加 @ 前缀
log.Information("收到订单 {@Order}", order);

{UserId} 不是占位符拼接——它被存为独立字段,日志平台可直接按 UserId 过滤。@ 前缀让对象按结构序列化(JSON 中展开为嵌套对象)而非调 ToString()。

{"@t":"2026-08-25T14:50:22.123Z","@m":"用户 42 从 10.0.0.8 登录","UserId":42,"Ip":"10.0.0.8"}

Sink:日志去向的生态系统

.WriteTo.Console()
.WriteTo.File("logs/app.log",
    rollingInterval: RollingInterval.Day,   // 按天滚动
    retainedFileCountLimit: 30)             // 保留 30 天
.WriteTo.Async(a => a.File("logs/async.log"))  // 异步写入

要点:滚动文件 sink 内建轮转(按天/按大小),无需外部 logrotate;Async 包装把写入挪到后台,高并发下保护业务线程。

Enricher:自动附加机器与进程上下文

.Enrich.WithMachineName()
.Enrich.WithProcessId()
.Enrich.WithThreadId()
.Enrich.FromLogContext()   // 配合 LogContext.PushProperty 动态加字段

LogContext.PushProperty("RequestId", rid) 可在 using 作用域内给所有日志附加字段——请求级上下文注入的标准做法。

JSON 输出:生产环境的必选项

.WriteTo.Console(new Serilog.Formatting.Json.JsonFormatter())
// 或更紧凑的:
.WriteTo.Console(new Serilog.Formatting.Compact.RenderedCompactJsonFormatter())

JSON 输出后,观测云 DataKit 采集即自动解析为字段——模板字段(UserId、Ip)直接可查。

ASP.NET Core 集成:UseSerilog

安装 Serilog.AspNetCore 后:

var builder = WebApplication.CreateBuilder(args);
builder.Host.UseSerilog((ctx, lc) => lc
    .ReadFrom.Configuration(ctx.Configuration)   // 从 appsettings.json 读取
    .Enrich.FromLogContext()
    .WriteTo.Console(new RenderedCompactJsonFormatter()));

UseSerilog 让 Serilog 接管整个宿主与框架日志(含请求日志中间件 UseSerilogRequestLogging,默认每请求一条带耗时的日志),appsettings.json 中的 Serilog 节点支持热改级别。

务必在程序退出时 Log.CloseAndFlush()——异步 sink 的缓冲日志靠它落盘,进程崩溃场景尤其重要。

观测云落地:Serilog 日志采集与分析

  1. 应用侧RenderedCompactJsonFormatter 输出 stdout(容器)或滚动文件(主机)。字段命名遵循团队契约。
  2. DataKit 采集:容器 stdout 自动采集;主机 conf.d/log/logging.conf 配置文件采集,source: dotnet-appservice 标识服务。JSON 自动解析为字段(@t/@m/@l 等 Serilog 字段在 Pipeline 中重命名标准化)。
  3. Pipeline 标准化@l(级别)映射标准 status@t 解析为 time
  4. 检索告警:日志查看器按 status/service/RequestId 过滤;监控器对 Error/Fatal 日志阈值告警,钉钉/企业微信/飞书触达。
  5. 链路关联:接入观测云 APM(.NET 探针)后 trace_id 经 LogContext 注入日志,与调用链双向跳转。
  6. 成本控制:多索引按级别拆保留,历史数据转发归档对象存储。

常见问题(FAQ)

Serilog 的静态 Log.Logger 和注入 ILogger 用哪个?

ASP.NET Core 项目推荐注入 ILogger<T>(UseSerilog 后底层就是 Serilog),可测试性更好;静态 Log.Logger 适合控制台程序与不便注入的场景。两者可共存。

为什么退出时丢日志?

异步 sink 的队列没落盘。程序入口用 try/finally { Log.CloseAndFlush(); } 兜底;ASP.NET Core 的 UseSerilog 已处理正常关停,但崩溃路径仍需注意。

如何在配置文件里管理级别而不改代码?

ReadFrom.Configuration + appsettings.json 的 Serilog:MinimumLevel 节点,改配置即生效(配合配置热加载可不重启)。环境间差异用环境变量覆盖。

敏感字段怎么防止进日志?

不要 Log.Information("{@User}", user) 无脑解构整个实体——密码、身份证都会进 JSON。做法:只解构白名单 DTO;自定义 IDestructuringPolicy 打码;平台侧再用观测云敏感数据扫描兜底。

系列阅读


获取专属方案

联系我们

加入社区

微信扫码
加入官方交流群

立即体验

在线开通,按量计费,真正的云服务!

立即开始

选择观测云版本

代码托管平台