CodeGym /课程 /C# SELF /结构化日志记录和 Serilog

结构化日志记录和 Serilog

C# SELF
第 64 级, 课程 1
可用

1. Introduction

如果普通的文本日志是你写的日记,记录着 "我今天吃了" 这样的备注,结构化日志把每条记录变成带字段的卡片:{日期: ..., 事件: "吃饭", 卡路里: 500, 菜品: "烤肉"}。这意味着你不仅能读日记,还能画出一个月的卡路里图表、按菜品过滤事件、查出什么时候吃得太晚。

Why is plain text not enough?

纯文本日志在开始时很方便……但很快会受限。想统计日志里的错误、统计电商的销售量、按用户 ID 跟踪某个用户的一系列操作(比如 42),如果所有数据都是一大串文本,会很难。结构化日志可以把分析和甚至 AI 挂上去,用来找异常、做仪表盘、自动响应问题。

Advantages

  • 可以记录不仅仅是消息,还有相关数据(字段/属性)。
  • 日志可以被自动分析:计数、过滤、生成报表。
  • 使用标准格式,比如 JSON,机器很容易解析。

Serilog: что это и почему он?

Serilog (официальный сайт: serilog.net, документация: github.com/serilog/serilog/wiki) — 一个在 .NET 里流行的结构化日志库。它能很好地和 Microsoft.Extensions.Logging 集成,支持把日志输出到很多系统(文件、Console、Seq、ElasticSearch、Grafana、Azure 等),对性能影响小,配置也简单。

Чем Serilog отличается от «просто логирования»?

  • Structure:日志是带字段的对象,可以按字段过滤(比如所有用户 ID 为 42 的错误)。
  • Formats:不仅能写文本,还能写 JSON、XML,便于后续处理。
  • Flexibility:有很多现成的 sink 包,可以把日志发到任何地方。

Structure записи лога с Serilog

先看一条结构化的日志,再写代码。

{
  "Timestamp": "2024-06-22T10:23:45.123Z",
  "Level": "Information",
  "MessageTemplate": "用户 {UserId} 登录系统",
  "Properties": {
    "UserId": 42,
    "IpAddress": "127.0.0.1"
  }
}

即使不深度分析也能看出来:这是用户 №42 以及他的 IP。

2. Установка и базовая настройка Serilog в C# проекте

Step 1. Installation of NuGet packages

在 Rider/Visual Studio 里通过 NuGet Package Manager 安装:

  • Serilog
  • Serilog.Sinks.Console(输出到控制台)
  • Serilog.Extensions.Logging(用于和 Microsoft.Extensions.Logging 集成)

通过命令行:

dotnet add package Serilog
dotnet add package Serilog.Sinks.Console

Step 2. Minimal configuration

在 Program.cs 里加配置并写第一条日志。

using System;
using Serilog;

namespace MySuperApp
{
    class Program
    {
        static void Main(string[] args)
        {
            // 1. Serilog 基本配置:输出到 Console
            Log.Logger = new LoggerConfiguration()
                .MinimumLevel.Debug()
                .WriteTo.Console()
                .CreateLogger();

            // 2. 示例结构化日志
            int userId = 42;
            string ip = "127.0.0.1";
            Log.Information("用户 {UserId} 使用 IP {IpAddress} 登录了系统", userId, ip);

            Log.CloseAndFlush();
        }
    }
}

在控制台大概会看到类似:

[10:30:16 INF] 用户 42 使用 IP 127.0.0.1 登录了系统

之后很容易把输出导到 JSON 文件、Seq 或其它系统。

3. Logging formatting: Message Template

在 Serilog 里不用字符串拼接,而用模板语法:

Log.Information("Operation {Operation} on file {FileName}", "deletion", "test.txt");

这不仅是好看的写法 —— 这是结构化日志:记录会包含字段 Operation 和 FileName,可以用于过滤和聚合。

Чем отличается от string.Format?

string.Format("Operation {0} on file {1}", operation, fileName) 只是把字符串拼起来,用占位符 {0}, {1}。而 Serilog 会把它们做成独立字段,便于后续分析。

Flexible setup: levels, filters, various "sinks"

Serilog 可以同时写到多个地方。

Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Debug()
    .WriteTo.Console()
    .WriteTo.File("log.txt", rollingInterval: RollingInterval.Day)
    .CreateLogger();

现在日志会同时写到控制台和按天滚动的文件。

4. Example

假设我们做一个控制台版「笔记」应用,能创建用户笔记。给它加上结构化日志。

using System;
using Serilog;

namespace NotesApp
{
    class Program
    {
        static void Main(string[] args)
        {
            Log.Logger = new LoggerConfiguration()
                .MinimumLevel.Information()
                .WriteTo.Console()
                .WriteTo.File("notes-log.json", rollingInterval: RollingInterval.Day, 
                              formatter: new Serilog.Formatting.Json.JsonFormatter())
                .CreateLogger();

            Console.WriteLine("请输入用户名:");
            string userName = Console.ReadLine();

            Log.Information("用户 {UserName} 启动了 NotesApp 应用", userName);

            while (true)
            {
                Console.WriteLine("请输入笔记内容(或输入 '退出'):");
                string note = Console.ReadLine();

                if (note == "退出")
                {
                    Log.Information("用户 {UserName} 结束了程序", userName);
                    break;
                }

                Log.Information("用户 {UserName} 创建了笔记:{NoteText}", userName, note);
            }

            Log.CloseAndFlush();
        }
    }
}

Comment:
日志会记录谁启动了程序、输入了什么、什么时候结束。在文件 notes-log.json 里每条记录都是一个 JSON 对象,便于分析。

5. Useful tips

Best practices for structured logging

  • 别在生产环境滥用 Debug/Trace 级别——要合理使用 Information 和 Warning。
  • 在模板里使用具名参数,不要用字符串拼接。
  • 切勿记录敏感数据(密码、token、密钥)。
  • 记录重要的业务事件,而不仅仅是错误和异常。
  • 配置日志轮转和清理,避免磁盘被写满。

Visualization and analysis: Seq, Kibana, Application Insights

Serilog 支持很多 sink —— 日志要发到的终点。

Sink 简短说明 使用场景
Console 直接输出到 Console 开发,测试
File 写入本地或网络文件 小项目,dev 环境
Seq 带过滤和仪表盘的 Web 界面 企业、分析
ElasticSearch 强大的存储与分析系统 大型公司
Azure Application Insights 云监控和遥测 在 Azure 上运行的服务

Seq (datalust.co/seq) — 在开发和内部使用中很受欢迎:过滤、按字段搜索很方便,部署也快。

Tables and visualization

下面是可以结构化记录的一些示例:

记录内容 在 Serilog 中的样子 示例值
用户 ID
{UserId}
123
动作
{Action}
"删除"
错误
Log.Error(ex, "Error in {Module}")
"注册模块"
操作耗时 {Elapsed:0.000} 秒 1.234
文件名
{FileName}
"report.pdf"

Интересные фишки и дополнительные возможности Serilog

  • Enrichers:给每条日志加入属性(比如 .Enrich.WithMachineName())。
  • Linked logs:加入 RequestId 来关联事件链。
  • Configuration via appsettings.json:适合生产环境配置。
{
  "Serilog": {
    "MinimumLevel": "Debug",
    "WriteTo": [
      { "Name": "Console" },
      { "Name": "File", "Args": { "path": "log.txt" } }
    ]
  }
}

高级 sink:还能把日志发到 Slack、Telegram、邮件(不过注意别把每个错误都变成一千封邮件)。

6. Практическая часть: интеграция с Microsoft.Extensions.Logging

在 .NET 里通常使用标准接口 ILogger,这样不绑定特定库。Serilog 可以作为一个 provider 接入。

Step 1. Package installation

dotnet add package Serilog.Extensions.Logging

Step 2. Configuring

using Microsoft.Extensions.Logging;
using Serilog;

// ...

// 像平常一样配置 Serilog:
Log.Logger = new LoggerConfiguration()
    .WriteTo.Console()
    .CreateLogger();

// 现在使用 Microsoft.Extensions.Logging
var loggerFactory = LoggerFactory.Create(builder =>
{
    builder.AddSerilog();
});

ILogger<Program> logger = loggerFactory.CreateLogger<Program>();
logger.LogInformation("测试消息:{TestValue}", 123);

// 别忘了关闭:
Log.CloseAndFlush();

Comment:
现在你用 ILogger<T> 写的代码就和具体提供者无关了——想换到 NLog 或 Log4Net 都可以。

7. Типичные ошибки при работе с Serilog

日志过载:如果什么都记录,重要的信息会很难找到。

把异常当文本记录:用带 exception 的重载,这样日志里会有异常结构。

try
{
    // some code
}
catch (Exception ex)
{
    Log.Error(ex, "执行请求时发生错误");
}

滥用配置:别把配置弄成一堆乱七八糟的东西——只加必要的 sink 和级别。

2
任务
C# SELF, 第 64 级, 课程 1
已锁定
使用 Serilog 进行基础结构化日志记录
使用 Serilog 进行基础结构化日志记录
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION