ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

C# | Serilog 新手入门

C# | Serilog 新手入门 C# Serilog 新手入门文章目录C# Serilog 新手入门1. 什么是第三方日志库与 Serilog1.1 概念讲解什么是日志为什么不能只写 Console.WriteLine1.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. 什么是第三方日志库与 Serilog1.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而是通过依赖注入使用微软官方抽象的ILoggerT接口记录日志。这样可以实现业务逻辑与具体日志实现框架的解耦。下面的架构图展示了 Serilog 在 DDD 系统中的位置与数据流向外部日志接收终端Cross-Cutting ConcernsInfrastructure LayerDomain LayerApplication LayerPresentation Layer调用调用数据持久化提供实现并注册依赖注入依赖注入依赖注入异步刷盘/推送网络发送 JSONOrdersControllerOrderAppServiceOrderDomainServiceDatabase ContextSerilog 配置与全局初始化Microsoft.Extensions.Logging.ILogger本地磁盘文件Seq 日志服务器 / ELKInfraLayer4. Serilog 动手实战4.1 第一行代码5 分钟跑通 Hello World第一步安装 NuGet 包打开 Visual Studio 的 NuGet 包管理器安装以下三个包Serilog核心库Serilog.Sinks.Console输出到控制台插件Serilog.Sinks.File输出到文件插件第二步编写极简控制台程序usingSerilog;classProgram{staticvoidMain(){// 1. 初始化 Serilog 配置Log.LoggernewLoggerConfiguration().MinimumLevel.Debug()// 设置最低日志记录级别.WriteTo.Console()// 输出到控制台.WriteTo.File(logs/myapp.txt,rollingInterval:RollingInterval.Day)// 按天生成日志文件.CreateLogger();// 2. 打出第一行日志Log.Information(Hello, Serilog! 欢迎来到结构化日志的世界。);// 3. 带有变量的结构化日志stringuserName张三;intage18;Log.Information(创建用户姓名{UserName}, 年龄{Age},userName,age);// 4. 确保程序退出前把内存中的日志刷入磁盘Log.CloseAndFlush();}}4.2 语法与配置详解4.2.1 结构化日志语法格式与核心组件Serilog 由三个核心概念组成Sinks输出接收端日志去哪里控制台、文件、数据库、Seq、Elasticsearch。Enrichers上下文增强器自动补全环境信息如当前线程 ID、机器名、环境名。Message Template消息模板定义变量提取规则。关键语法技巧与$占位符varusernew{Id1001,Name李四,RoleAdmin};// 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.LoggernewLoggerConfiguration().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中挂载varbuilderWebApplication.CreateBuilder(args);// 声明让 ASP.NET Core 读取配置文件并使用 Serilog 接管官方日志系统builder.Host.UseSerilog((context,services,configuration)configuration.ReadFrom.Configuration(context.Configuration).ReadFrom.Services(services));varappbuilder.Build();// 在 Controller 中即可通过标准 ILoggerT 注入直接使用 Serilog 的强大功能5. 总结学习 Serilog 不仅仅是学会安装一个 NuGet 包更是从传统文本运维向现代化数据驱动运维转变的关键一步。抛弃旧观念不要再使用Console.WriteLine或简单的字符串拼接写日志。掌握核心价值Serilog 的灵魂在于结构化把日志当作带属性的事件数据保存。架构规范业务代码中依赖微软抽象的ILogger通过依赖注入在基础设施层配置 Serilog实现优雅的架构解耦。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进