要说 .NET 生态里最受欢迎的日志库,Serilog 绝对排得上号。它设计得相当精巧——API 丰富、架构灵活,输出端和格式化器都是可插拔的,开发者几乎可以按需定制。下面就从基础概念开始,一步步把它拆开看看。
1. 日志级别
Serilog 按严重程度把日志分为六个级别,从高到低排列:
- Fatal:程序已经无法继续跑,必须立刻处理。
- Error:出了错误,需要介入修复。
- Warning:值得警惕,但暂时不用动。
- Information:提供一些有用的状态信息,通常用来跟踪流程。
- Debug:调试用的,帮开发者定位问题。
- Verbose:最详细的日志,适合排查复杂疑难。
2. 日志输出
Serilog 原生支持多种输出目标(也就是 Sink),用得比较多的有:
- Console:直接打印到控制台。
- File:写到文件里。
- Seq:发送到日志收集平台 Seq。
- Elasticsearch:写入 Elasticsearch 做集中存储和搜索。
当然,如果这些都不够用,还可以自己写一个自定义输出器。
3. 日志格式
Serilog 提供了几种格式化日志消息的方式:
- 简单文本格式:最常见的纯文本输出。
- JSON 格式:适合做结构化日志,便于后续解析。
- Message Templates 格式:一种非常灵活的方式,支持在消息里嵌入占位符,既能保持可读性,又能做到结构化。
4. 安装
通过 NuGet 直接安装就行:
Install-Package Serilog
5. 基础使用示例
用 Serilog 写日志其实很简单。下面是一个最基础的控制台输出例子:
using Serilog;
class Program
{
static void Main()
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console()
.CreateLogger();
Log.Information("Hello, Serilog!");
Log.CloseAndFlush();
}
}
运行之后,控制台就会打印出 Hello, Serilog! 这条信息。
6. 日志级别示例
设置不同的最小级别,就能控制哪些日志会被输出。下面的例子把级别降到 Verbose,然后依次记录了各个级别的消息:
using Serilog;
class Program
{
static void Main()
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Verbose()
.WriteTo.Console()
.CreateLogger();
Log.Verbose("This is a verbose log message.");
Log.Debug("This is a debug log message.");
Log.Information("This is an informational log message.");
Log.Warning("This is a warning log message.");
Log.Error("This is an error log message.");
Log.Fatal("This is a fatal log message.");
Log.CloseAndFlush();
}
}
7. 消息模板
消息模板是 Serilog 的一大亮点。你可以在日志中插入 {Name} 这样的占位符,运行时会被实际值替换:
using Serilog;
class Program
{
static void Main()
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console(outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}")
.CreateLogger();
Log.Information("Hello, {Name}!", "Serilog");
Log.CloseAndFlush();
}
}
输出效果类似这样:
2023-09-15 22:23:54.576 +08:00 [INF] Hello, Serilog!
8. 日志属性
除了简单的占位符,你还可以在日志里附加更复杂的属性——比如用匿名对象:
using Serilog;
class Program
{
static void Main()
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console()
.CreateLogger();
Log.Information("Processed {@Count} records in {Time} ms.", new { Count = 10, Time = 123 });
Log.CloseAndFlush();
}
}
输出会把这个匿名对象的字段展开:
Processed { Count: 10, Time: 123 } records in 0 ms.
9. 文件输出配置
把日志写到文件里也很常见。通过 rollingInterval 可以控制文件按天滚动:
Log.Logger = new LoggerConfiguration()
.WriteTo.File(
$"logs\\log-.txt",
rollingInterval: RollingInterval.Day,
outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}")
.CreateLogger();
Log.Information("This is a log message!");
Log.CloseAndFlush();
这样每天会生成一个类似 log-20230914.txt 的文件,非常便于按日期归档。
10. 结构化日志记录
Serilog 支持真正的结构化日志——你可以直接把一个对象整体写进去,它会自动序列化并保留结构:
using Serilog;
class Program
{
static void Main()
{
var weather = new WeatherForecast
{
Date = DateTime.Now,
TemperatureC = 22,
Summary = "Sunny"
};
Log.Information("Weather forecast: {@Weather}", weather);
Log.CloseAndFlush();
}
}
public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureC { get; set; }
public string Summary { get; set; }
}
输出结果会是一个 JSON 风格的字符串,保留所有属性和类型:
Weather forecast: {"Date":"2023-09-15T22:39:53.8634787+08:00","TemperatureC":22,"Summary":"Sunny","$type":"WeatherForecast"}
11. 日志过滤
有时候只想输出特定级别的日志,Serilog 的过滤器可以帮上忙。比如只保留 Error 及以上的消息:
using Serilog;
using Serilog.Events;
class Program
{
static void Main()
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console()
.Filter.ByIncludingOnly(logEvent => logEvent.Level >= LogEventLevel.Error)
.CreateLogger();
Log.Verbose("This is a verbose log message.");
Log.Error("This is an error log message.");
Log.CloseAndFlush();
}
}
运行后,控制台只会输出 Error 级别的消息,其他级别的都被过滤掉了。
12. 扩展与自定义输出器
如果内置的 Sink 不够用,完全可以自己写一个。下面是一个自定义控制台输出器的例子:
using Serilog;
using Serilog.Configuration;
using Serilog.Events;
public static class CustomConsoleSinkExtensions
{
public static LoggerConfiguration CustomConsole(
this LoggerSinkConfiguration sinkConfiguration,
ITextFormatter formatter = null,
LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum)
{
return sinkConfiguration.Sink(
new CustomConsoleSink(formatter),
restrictedToMinimumLevel);
}
}
public class CustomConsoleSink : ILogEventSink
{
private readonly ITextFormatter _formatter;
public CustomConsoleSink(ITextFormatter formatter)
{
_formatter = formatter ?? throw new ArgumentNullException(nameof(formatter));
}
public void Emit(LogEvent logEvent)
{
var message = new StringWriter();
_formatter.Format(logEvent, message);
Console.WriteLine(message.ToString());
}
}
然后通过扩展方法加入到配置链中:
Log.Logger = new LoggerConfiguration()
.WriteTo.CustomConsole()
.CreateLogger();
这样一来,日志输出的目的地、格式都可以完全由你掌控。
13. 总结
Serilog 在 .NET 日志领域能做到如此受欢迎,不是没有道理的。它提供了丰富的输出方式、灵活的格式化选项,以及强大的可扩展性。无论是简单的控制台输出,还是复杂的结构化日志和自定义 Sink,它都能轻松应对。希望这篇文章能帮你更顺畅地用好这个库。