C# | Serilog 新手入门
C# Serilog 新手入门
文章目录
- C# Serilog 新手入门
- 1. 什么是第三方日志库与 Serilog
- 1.1 概念讲解:什么是日志?为什么不能只写 `Console.WriteLine`?
- 1.2 C# 生态中的优秀日志库对比
- 1.3 C# 日志技术的演进历程
- 2. Serilog 结构化日志与传统日志的对比
- 2.1 结构化日志(Structured Logging)的核心特点
- 传统日志代码与产出
- Serilog 结构化日志代码与产出
- 2.2 Serilog 在代码与日志查询上的优势
- 2.3 帮助小白快速理解的心智模型
- 3. 引入 Serilog 的意义与架构定位
- 3.1 实际应用场景与效率提升
- 3.2 软件架构中的定位
- 4. Serilog 动手实战
- 4.1 第一行代码:5 分钟跑通 Hello World
- 第一步:安装 NuGet 包
- 第二步:编写极简控制台程序
- 4.2 语法与配置详解
- 4.2.1 结构化日志语法格式与核心组件
- 4.2.2 两种配置方式:Fluent API 代码配置 vs `appsettings.json` 声明式配置
- 方式一:Fluent API(代码链式调用,适合小型项目或控制台应用)
- 方式二:`appsettings.json`(推荐!适合 ASP.NET Core 项目,灵活可配置)
- 5. 总结
针对刚接触 C# 开发、从未独立使用过第三方日志库的新手,很多关于 Serilog 的官方文档过于偏向 API 说明,容易让人“知其然不知其所以然”。
本教程参考了模块化的递进架构,用通俗直白的语言带你彻底搞懂Serilog到底强在哪?以及如何在真实系统中合理定位它。
1. 什么是第三方日志库与 Serilog
1.1 概念讲解:什么是日志?为什么不能只写Console.WriteLine?
在刚学 C# 时,很多人习惯用Console.WriteLine("用户登录成功")来查看程序运行状态。但在真实的企业级项目(如 Web API、后台服务)中,这种做法存在巨大隐患:
- 数据随用随丢:程序重启或控制台窗口一关,输出的信息全部消失。
- 没有级别区分:无法区分普通提示(Information)、警告(Warning)还是系统崩溃(Fatal)。
- 阻塞性能:直接向控制台频繁写数据是同步且昂贵的 I/O 操作,会导致高并发下程序卡死。
- 缺乏上下文:只写一句“报错了”,你不知道是谁在什么时间、哪条线程、哪个请求上报的错。
第三方日志库就是为此而生的专业管家。它能够异步、高性能地将程序运行过程中的重要状态记录下来,并自动附带时间戳、线程号、日志级别,最终将日志输出到文件、数据库或远程日志中心进行长期保存。
1.2 C# 生态中的优秀日志库对比
C# 生态发展至今,出现了几款经典的第三方日志库:
| 日志库 | 历史与特点 | 适用场景 |
|---|---|---|
| Log4net | 移植自 Java 的 Log4j,历史最悠久,古老且稳定。 | 老旧项目的维护。由于配置繁琐(大量 XML)、缺少现代化特性,新项目不推荐。 |
| NLog | 性能优异,配置灵活,在 C# 生态中流行多年。 | 各种中大型传统项目,功能非常强大。 |
| Serilog | 现代化日志库首选,天然为“结构化日志”而生,社区最活跃。 | 全新项目的首选,特别是结合 Seq、ELK 等现代日志分析平台的项目。 |
1.3 C# 日志技术的演进历程
C# 日志技术经历了四个主要阶段:
- 原始时代:直接使用
Console.WriteLine()或System.Diagnostics.Trace,功能极其原始。 - 文本日志时代(Log4net / NLog 早期):开始将日志格式化写入文本文件(如
.log文件),但日志内容全是一串串拼接的字符串。 - 接口抽象时代(Microsoft.Extensions.Logging):微软推出了官方日志抽象接口
ILogger,框架只定义标准,具体实现交由第三方库(如 Serilog)完成。 - 结构化日志时代(Serilog 领衔):不再把日志当作“一句话”,而是当作“带属性的 JSON 数据对象”,掀起了现代化运维与排查的变革。
2. Serilog 结构化日志与传统日志的对比
2.1 结构化日志(Structured Logging)的核心特点
传统日志输出的是非结构化纯文本,而 Serilog 输出的是结构化键值数据。
传统日志代码与产出
// 传统拼接字符串logger.Info("用户 "+userId+" 在 "+DateTime.Now+" 购买了商品 "+productId);// 产出文本: 用户 10086 在 2026-08-09 10:00:00 购买了商品 9988当你在数 GB 的日志文件中想要检索“用户 10086 买了哪些商品”时,只能用复杂的正则表达式或全文逐字匹配,非常缓慢且容易误判。
Serilog 结构化日志代码与产出
// Serilog 消息模板(注意变量名前的名称)logger.Information("用户 {UserId} 购买了商品 {ProductId}",userId,productId);Serilog 在后台不仅会生成人眼可读的文本,还会同时保留数据的原始结构:
{"Timestamp":"2026-08-09T10:00:00Z","Level":"Information","MessageTemplate":"用户 {UserId} 购买了商品 {ProductId}","Properties":{"UserId":10086,"ProductId":9988}}2.2 Serilog 在代码与日志查询上的优势
- 像查 SQL 数据库一样查日志:因为属性被单独提取保存了,在 Seq 或 Elasticsearch 等日志系统中,你可以直接输入
Properties.UserId == 10086或Properties.ProductId > 1000实施精确过滤。 - 复杂对象的原生拆解:如果你传入一个 C# 对象,在变量名前加上
@符号(如{@User}),Serilog 会自动将其序列化为 JSON 展开存储,无需手动JsonConvert.SerializeObject。
2.3 帮助小白快速理解的心智模型
- ❌传统日志的心智模型:把日志当成记事本,疯狂向里面追加一行行打印出来的文本。
- ✅Serilog 的心智模型:把打日志当成向一个无模式(NoSQL)数据库插入事件记录。你每一次记录日志,都是在发起一次数据结构收集。
3. 引入 Serilog 的意义与架构定位
3.1 实际应用场景与效率提升
- 生产环境快速排错:系统报错时,无需猜测参数,结构化日志直接呈现当时请求入参的完整 JSON 数据。
- 性能瓶颈追踪:通过在日志中附带
ElapsedMilliseconds,可以秒级筛选出“执行时间大于 2000ms”的所有数据库查询日志。 - 安全审计与业务分析:记录关键业务事件(如支付、修改密码),方便日后查账或统计用户行为轨迹。
3.2 软件架构中的定位
在遵循领域驱动设计(DDD)或分层架构的系统里,日志属于典型的横切关注点(Cross-Cutting Concerns)。
- 配置与初始化:位于系统的最外层(基础设施层/启动入口)。
- 业务层使用:业务层(应用层、领域层)不直接依赖 Serilog,而是通过依赖注入使用微软官方抽象的
ILogger<T>接口记录日志。这样可以实现业务逻辑与具体日志实现框架的解耦。
下面的架构图展示了 Serilog 在 DDD 系统中的位置与数据流向:
4. Serilog 动手实战
4.1 第一行代码:5 分钟跑通 Hello World
第一步:安装 NuGet 包
打开 Visual Studio 的 NuGet 包管理器,安装以下三个包:
Serilog(核心库)Serilog.Sinks.Console(输出到控制台插件)Serilog.Sinks.File(输出到文件插件)
第二步:编写极简控制台程序
usingSerilog;classProgram{staticvoidMain(){// 1. 初始化 Serilog 配置Log.Logger=newLoggerConfiguration().MinimumLevel.Debug()// 设置最低日志记录级别.WriteTo.Console()// 输出到控制台.WriteTo.File("logs/myapp.txt",rollingInterval:RollingInterval.Day)// 按天生成日志文件.CreateLogger();// 2. 打出第一行日志Log.Information("Hello, Serilog! 欢迎来到结构化日志的世界。");// 3. 带有变量的结构化日志stringuserName="张三";intage=18;Log.Information("创建用户:姓名={UserName}, 年龄={Age}",userName,age);// 4. 确保程序退出前把内存中的日志刷入磁盘Log.CloseAndFlush();}}4.2 语法与配置详解
4.2.1 结构化日志语法格式与核心组件
Serilog 由三个核心概念组成:
- Sinks(输出接收端):日志去哪里?(控制台、文件、数据库、Seq、Elasticsearch)。
- Enrichers(上下文增强器):自动补全环境信息(如:当前线程 ID、机器名、环境名)。
- Message Template(消息模板):定义变量提取规则。
关键语法技巧:@与$占位符
varuser=new{Id=1001,Name="李四",Role="Admin"};// 1. 默认处理:Serilog 会调用 user.ToString(),通常输出类名Log.Information("用户信息:{User}",user);// 2. 加上 @ 符号(Destructuring 解构):把对象展开为 JSON 存储Log.Information("用户信息:{@User}",user);// 3. 加上 $ 符号:强制转换为字符串(即使是复杂对象)Log.Information("用户信息:{$User}",user);4.2.2 两种配置方式:Fluent API 代码配置 vsappsettings.json声明式配置
方式一:Fluent API(代码链式调用,适合小型项目或控制台应用)
Log.Logger=newLoggerConfiguration().MinimumLevel.Information().Enrich.WithThreadId()// 需要安装 Serilog.Enrichers.Thread 包.WriteTo.Console(outputTemplate:"[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} (Thread:{ThreadId}){NewLine}{Exception}").CreateLogger();方式二:appsettings.json(推荐!适合 ASP.NET Core 项目,灵活可配置)
需要安装包:Serilog.Settings.Configuration和Serilog.AspNetCore。
1. 修改appsettings.json文件:
{"Serilog":{"MinimumLevel":{"Default":"Information","Override":{"Microsoft":"Warning","System":"Warning"}},"WriteTo":[{"Name":"Console"},{"Name":"File","Args":{"path":"logs/log-.txt","rollingInterval":"Day","retainedFileCountLimit":30}}],"Enrich":["FromLogContext","WithMachineName","WithThreadId"]}}2. 在 ASP.NET CoreProgram.cs中挂载:
varbuilder=WebApplication.CreateBuilder(args);// 声明让 ASP.NET Core 读取配置文件并使用 Serilog 接管官方日志系统builder.Host.UseSerilog((context,services,configuration)=>configuration.ReadFrom.Configuration(context.Configuration).ReadFrom.Services(services));varapp=builder.Build();// 在 Controller 中即可通过标准 ILogger<T> 注入直接使用 Serilog 的强大功能5. 总结
学习 Serilog 不仅仅是学会安装一个 NuGet 包,更是从传统文本运维向现代化数据驱动运维转变的关键一步。
- 抛弃旧观念:不要再使用
Console.WriteLine或简单的字符串拼接写日志。 - 掌握核心价值:Serilog 的灵魂在于结构化,把日志当作带属性的事件数据保存。
- 架构规范:业务代码中依赖微软抽象的
ILogger,通过依赖注入在基础设施层配置 Serilog,实现优雅的架构解耦。
