Serilog使用指南

引言

Serilog 是一个 .NET 平台上的强大的日志记录库。它提供了丰富的 API 以及可插拔的日志格式化器和输出器,使得在 .NET 应用程序中实现可定制化的、可扩展的日志记录变得轻而易举。

在本文中,我们将探讨 Serilog 的一些基础知识、API、配置和示例。

基础知识

日志级别

Serilog 支持多个日志级别,包括以下级别(按照严重程度从高到低排列):

  • Fatal: 程序已经无法继续运行,需要立即解决的问题。
  • Error: 一个错误发生,需要被处理。
  • Warning: 一个警告,通常需要被留意,但是不需要立即处理。
  • Information: 提供有用的信息,通常只有在调试应用程序时才需要关注。
  • Debug: 提供调试信息,有助于调试应用程序。
  • Verbose: 提供大量的细节信息,通常只用于调试复杂的问题。

日志输出

Serilog 支持多种日志输出,包括:

  • Console(控制台输出)
  • File(文件输出)
  • Seq(通过 Seq (opens new window)输出到日志收集器)
  • Elasticsearch(通过 Elasticsearch (opens new window)输出到搜索引擎)

此外,Serilog 还支持自定义日志输出器。

日志格式

Serilog 支持多种日志格式化方式,包括:

  • 简单文本格式
  • JSON 格式
  • Message Templates 格式(一种更加灵活的格式)

安装

可以通过 NuGet 安装 Serilog。

Install-Package Serilog

使用

基础使用

在应用程序中使用 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! 输出到控制台。

详细使用

日志级别

下面的示例演示了如何在 Serilog 中设置不同的日志级别:

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();
}
}
消息模板

Serilog 采用消息模板来格式化日志消息。下面是一个简单的消息模板示例:

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();
}
}

在上面的示例中,我们使用了一个带有模板的控制台输出器,并且在消息模板中使用了占位符 {Name}。当日志记录方法被调用时,{Name} 将被替换为 Serilog。我们可以看到 Hello, Serilog! 在控制台输出。

日志属性

Serilog 支持日志属性,这使得我们可以在日志消息中记录更多的信息。下面的示例演示了如何在日志消息中添加属性:

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();
}
}

在上面的示例中,我们使用了一个匿名类型来表示日志属性。在日志消息中,我们使用了 @ 符号来引用这个匿名类型。当日志记录方法被调用时,Serilog 将自动将匿名类型的属性添加到日志消息中。上面的代码将输出 Processed { Count: 10, Time: 123 } records in 0 ms.

输出模板定义
  • outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}")

这个也是官方的默认模板,我们可以这个扩展

.WriteTo.File("log.txt",
outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}")
输出到文件
  • rollingInterval: RollingInterval.Day 每天一个日志文件
  • outputTemplate 输出格式模板
.WriteTo.File(
$"logs\\log-.txt" ,
//每天一个文件 ,生成类似:log-20230914.txt
//fileSizeLimitBytes: null 文件限制大小:无 如果不配置默认是:1GB 1GB就不记录了
//retainedFileCountLimit: null 保留文件数量 如果不配置只保留31天的日志
//https://github.com/serilog/serilog-sinks-file
rollingInterval: RollingInterval.Day , retainedFileCountLimit: null ,
//fileSizeLimitBytes: null,
//单个文件大小: 1024000 1024000是1M
//rollOnFileSizeLimit: true 就是滚动文件,如果超过单个文件大小,会滚动文件 产生类似:log.txt log_001.txt log_002.txt
fileSizeLimitBytes: 3024000 , rollOnFileSizeLimit: true ,
//非必填:指定最小等级
restrictedToMinimumLevel: LogEventLevel.Information ,
//非必填: 也可以指定输出格式:这种格式好像与系统默认没有什么区别
//outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}"

//outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception} {NewLine}{Version}{myval}"

outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception} {NewLine}{Version}{myval} {NewLine}{UserId}{myid}{NewLine}"
)
结构化记录日志
写入变量
var itemNumber = 10;
var itemCount = 999;

// 使用占位符写入
// 结果:2023-09-15 22:23:54.576 +08:00 [INF] Processing item 10 of 999
this._logger.LogDebug( "Processing item {ItemNumber} of {ItemCount}" , itemNumber , itemCount );
写入类

特别提示:记录对象后:

1.日志中会多一个$type属性

2.日期类型数据格式化后都是这样格式:2023-09-16T22:26:27.5905512+08:00

var wf = new WeatherForecast
{
Date = DateTime.Now.AddDays( 1 ) ,
TemperatureC = 55 ,
Summary = ""
};

// @表示一个对象 这样就可以把一个对象直接传递进去
//结果:2023-09-15 22:26:27.601 +08:00 [INF] WeatherForecast 的数据 {"Date":"2023-09-16T22:26:27.5905512+08:00","TemperatureC":55,"TemperatureF":130,"Summary":"","$type":"WeatherForecast"}
this._logger.LogInformation( "WeatherForecast 的数据 {@wf}" , wf );
写入集合
List<string> list1 = new List<string>() { "q1" , "q2" };

//写入集合
//结果:2023-09-15 22:36:46.751 +08:00 [INF] 集合的数据 ["q1","q2"]
this._logger.LogInformation( "集合的数据 {@list1}" , list1 );
List<WeatherForecast> listw = new List<WeatherForecast>() {
new WeatherForecast
{
Date = DateTime.Now.AddDays( 1 ) ,
TemperatureC = 11 ,
Summary = "one"
},
new WeatherForecast
{
Date = DateTime.Now.AddDays( 2 ) ,
TemperatureC = 22 ,
Summary = "two"
}};

//写入集合
//结果: 2023-09-15 22:39:53.863 +08:00 [INF] 集合的数据 [{"Date":"2023-09-16T22:39:53.8634787+08:00","TemperatureC":11,"TemperatureF":51,"Summary":"one","$type":"WeatherForecast"},{"Date":"2023-09-17T22:39:53.8634842+08:00","TemperatureC":22,"TemperatureF":71,"Summary":"two","$type":"WeatherForecast"}]
this._logger.LogInformation( "集合的数据 {@listw}" , listw );
写入匿名类
var user = new
{
Name = "Nick" ,
Id = "nblumhardt" ,
add = new List<string>() { "add1" , "add2" } ,
man = new
{
age = 1 ,
names = "qq"
}
};

// @表示一个对象(上面这个是匿名类也可以写入的)
//结果:2023-09-15 22:23:54.576 +08:00 [INF] Logged on user {"Name":"Nick","Id":"nblumhardt","add":["add1","add2"],"man":{"age":1,"names":"qq"}}
this._logger.LogInformation( "Logged on user {@user}" , user );