Serilog 2.10 中文文档
Posted JimCarter
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Serilog 2.10 中文文档相关的知识,希望对你有一定的参考价值。
文章目录
本文基于发稿时的最新版本,Serilog: 2.10
1. 快速开始
1.1 控制台应用
这里以控制台应用为例,首先安装以下三个nuget包:
Serilog
Serilog.Sinks.Console
Serilog.Sinks.File
(可以用命令行方式安装或通过VS安装,随意)
第二个nuget包,用来将日志输出到控制台。第三个用来将日志输出到文件。
然后,修改代码如下:
static void Main()
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()//最小日志等级
//日志打印到控制台
.WriteTo.Console()
//日志打印到文件上
.WriteTo.File("logs/myapp.txt", rollingInterval: RollingInterval.Day)
//按日期生成日志路径,需要安装nuget: Serilog.Sinks.Map
.WriteTo.Map(
le => le.Timestamp.Date,
(d, lc) => lc.File($"logs/d:yyyyMMdd/log.txt"); )
.CreateLogger();
Log.Information("Hello, world!");
int a = 10, b = 0;
try
Log.Debug("Dividing A by B", a, b);
Console.WriteLine(a / b);
catch (Exception ex)
Log.Error(ex, "Something went wrong");
finally
Log.CloseAndFlush();
1.2 在ASP.NET Core应用中使用Serilog
1.2.1 安装nuget包
首先安装Serilog.AspNetCor
包
1.2.2 修改Program.cs
代码
public static int Main(string[] args)
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
.Enrich.FromLogContext()
.WriteTo.Console()
.CreateLogger();
try
Log.Information("Starting web host");
CreateHostBuilder(args).Build().Run();
return 0;
catch (Exception ex)
Log.Fatal(ex, "Host terminated unexpectedly");
return 1;
finally
Log.CloseAndFlush();
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseSerilog() // 添加Serilog
.ConfigureWebHostDefaults(webBuilder =>
webBuilder.UseStartup<Startup>();
);
1.2.3 删除appsettings.json
里的Logging
节点
删除之后,即可使用
1.2.4 两步初始化
在上面的例子中我们在程序启动时就初始化了Serilog,这样做好处是可以捕获到Host配置的异常,但没法使用appsettings.json
和依赖注入。
所以为了能够使用配置文件和依赖注入,Serilog支持第二次初始化(两步初始化)。通过在UseSerilog
里配置回调实现:
首先,将CreateLogger
改为CreateBootstrapLogger
:
public static int Main(string[] args)
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
.Enrich.FromLogContext()
.WriteTo.Console()
.CreateBootstrapLogger(); // 修改此项
然后,在UserSerilog
里配置回调:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseSerilog((context, services, configuration) => configuration
.ReadFrom.Configuration(context.Configuration)
.ReadFrom.Services(services)
.Enrich.FromLogContext()
.WriteTo.Console())
.ConfigureWebHostDefaults(webBuilder =>
webBuilder.UseStartup<Startup>();
);
2. 基本配置
2.1 Sink
上面的实例代码的WriteTo.Console()
和WriteTo.File()
,就是不同的sinker。用来控制把日志写入到哪里。除了控制台和文件系统,你还可以通过安装不同的nuget包把日志写入各种存储系统,如数据库、消息队列、AWS、Azure、邮箱、HTTP等很多地方。
2.2 输出模板
.WriteTo.File("log.txt",
outputTemplate: "Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz [Level:u3] Message:ljNewLineException")
默认的输出模板就是上面的样子,Timestamp
和Level
都是内置属性。Message:lj
表示将消息序列化成json字符串,string类型除外(j
表示json,l
表示except for string literals)。
Level:u3
表示将日志等级的输出显示为3个大写字符,如DBG
、INF
、ERR
等。Level:w3
表示三个字符的小写。
同理,可以增加一个Properties:j
用来显示额外的上下文信息。
2.3 日志等级
同大多数的日志框架一样,分为Verbose
、Debug
、Information
、Warning
、Error
和Fatal
六个等级。在之前的例子中我们可以看到,使用.MinimumLevel.Debug()
配置了最低等级,小于此等级的日志不会被打印出来。
默认日志等级:如果没有配置MinimumLevel
的话,默认等级为Information
。
2.3.1 日志等级判断
可以通过Log.IsEnabled(LogEventLevel.Debug)
来判断某个等级是否启用。
2.3.2 动态日志等级
var levelSwitch = new LoggingLevelSwitch();
levelSwitch.MinimumLevel = LogEventLevel.Warning;
var log = new LoggerConfiguration()
.MinimumLevel.ControlledBy(levelSwitch)
.WriteTo.ColoredConsole()
.CreateLogger();
当需要切换日志等级时,直接修改:
levelSwitch.MinimumLevel = LogEventLevel.Verbose;
log.Verbose("This will now be logged");
2.4 不同级别的日志输出到不同的地方
我们可以通过配置不同sinker的restrictedToMinimumLevel
的属性,来让不同级别的日志落到不同的sinker上。
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.File("log.txt")
.WriteTo.Console(restrictedToMinimumLevel: LogEventLevel.Information)
.CreateLogger();
以上代码会将所有的level的日志输出到log.txt
上,但只有Information
及以上级别的日志会输出到Console
上。
看到这里,也许你会问:MinimumLevel.Debug()
和sinker里的restrictedToMinimumLevel
有什么区别?
其实MinimumLevel.Debug()
只是来负责控制哪些级别的日志可以触发WriteTo
操作,而restrictedToMinimumLevel
只是用来根据级别过滤这些日志。如果MinimumLevel
设置为Information
,即使restrictedToMinimumLevel
设置为Debug
,最终也不会看到Debug级别的日志。
2.5 Enrichers
输出模板里我们介绍过Timestamp:yyyy
、Level
都属于Enricher,只不过这些都是框架内置的。我们也可以定义自己的Enricher来打印自定义的内容,如下就是一个需要打印出线程Id的Enricher:
class ThreadIdEnricher : ILogEventEnricher
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty(
"ThreadId", Thread.CurrentThread.ManagedThreadId));
使用,通过ThreadId
:
Log.Logger = new LoggerConfiguration()
.Enrich.With(new ThreadIdEnricher())
.WriteTo.Console(
outputTemplate: "Timestamp:HH:mm [Level] (ThreadId) MessageNewLineException")
.CreateLogger();
如果你想打印的ThreadId
是固定的,就不用定义ThreadIdEnricher
类,可以直接这么写:
Log.Logger = new LoggerConfiguration()
.Enrich.WithProperty("ThreadId","123")
.WriteTo.Console(
outputTemplate: "Timestamp:HH:mm [Level] (ThreadId) MessageNewLineException")
.CreateLogger();
框架支持的Enricher有以下几种:
名称 | nuget包 |
---|---|
WithMachineName() 和WithEnvironmentUserName() | Serilog.Enrichers.Environment |
WithProcessId() | Serilog.Enrichers.Process |
WithThreadId() | Serilog.Enrichers.Thread |
WithHttpRequestId() | Serilog.Web.Classic |
WithExceptionDetails() | Serilog.Exceptions |
WithDemystifiedStackTraces() | Serilog.Enrichers.Demystify |
WithCorrelationId() | Serilog.Enrichers.CorrelationId |
WithClientIp() 、WithClientAgent() | Serilog.Enrichers.ClientInfo |
WithXllPath() | Serilog.Enrichers.ExcelDna |
WithSensitiveDataMasking() | Serilog.Enrichers.Sensitive |
FromGlobalLogContext() | Serilog.Enrichers.GlobalLogContext |
2.6 根据Enricher的值进行过滤
Log.Logger = new LoggerConfiguration()
.WriteTo.Console()
.Filter.ByExcluding(Matching.WithProperty<int>("Count", p => p < 10))
.CreateLogger();
当Count
的值小于10时,不会打印日志。
2.7 Sub-logger
Log.Logger = new LoggerConfiguration()
.WriteTo.Console()
.WriteTo.Logger(lc => lc
.Filter.ByIncludingOnly(...)
.WriteTo.File("log.txt"))
.CreateLogger();
个人感觉是sinker加上filter。
2.8 从配置文件读取配置
可以通过 Serilog.Settings.AppSettings
或 Serilog.Settings.Configuration
两个nuget包实现。
3. 结构化数据
-
对于
int
、bool
、string
、Guid
、Uri
等简单的数据类型,Serilog可以转为字符串输出。 -
对于
List
、Dictionary
等集合类型,会自动序列化为json字符串。 -
对于复杂对象,可以使用
@
操作符将对象序列化为json字符串,否则会直接调用ToString()
方法。如有以下代码:
var person = new Person Name = "aa", FirstName = "bb", Id = 5 ;
Log.Information("Processing Person", person);
Log.Information("Processing @Person", person);
输出为:
[16:22:33 INF] Processing CalcStringDuplicated.Person
[16:22:33 INF] Processing "Name": "aa", "FirstName": "bb", "Id": 5, "$type": "Person"
如何自定义数据的输出结构?
可以使用ByTransforming
,比如下述代码我们只需要把Person
对象的Name
和Id
属性输出:
Log.Logger = new LoggerConfiguration()
.Destructure.ByTransforming<Person>(
r => new Name = r.Name, Id = r.Id )
.WriteTo...
输出为:
[16:27:39 INF] Processing CalcStringDuplicated.Person
[16:27:39 INF] Processing "Name": "aa", "Id": 5
强制将object转为string
上面说到对于List
等集合类型,Serilog会自动序列化为json字符串,如果不想这么做,可以使用$
操作符:
var c =new List<int>10;
Log.Information("value = $c", c);
输出为:
[16:35:13 INF] value = System.Collections.Generic.List`1[System.Int32]
4. 消息模板
Serilog建议使用消息模板将日志展示出来,而不是直接展示消息。
// 不推荐
Log.Information("The time is " + DateTime.Now);
// 推荐做法
Log.Information("The time is Now", DateTime.Now);
这个模板的语法类似于string.format()
. 大括号里的属性命名规则同普通属性一致,建议使用Pascal命名,但你也可以随便写。
属性和后面参数对象是根据先后位置对应的,而不是根据名称。
以下代码:
var person = new Person Name = "aa", FirstName = "bb", Id = 5 ;
var person2 = new Person Name = "aa2", FirstName = "22", Id = 5 ;
Log.Information("Processing @Person2---@Person",person, person2);
Log.Information("Processing @person---@person2", person, person2);
Log.Information("Processing @person2---@person", person, person2);
Log.Information("Processing @a---@b", person, person2);
输出结果都一样:
[17:21:58 INF] Processing "Name": "aa", "Id": 5---"Name": "aa2", "Id": 5
[17:21:58 INF] Processing "Name": "aa", "Id": 5---"Name": "aa2", "Id": 5
[17:21:58 INF] Processing "Name": "aa", "Id": 5---"Name": "aa2", "Id": 5
[17:21:58 INF] Processing "Name": "aa", "Id": 5---"Name": "aa2", "Id": 5
消息模板也支持类似string.format
的01
。如:
Log.Information("Processing @1---@0", person, person2);
输出为:
[17:24:07 INF] Processing "Name": "aa2", "Id": 5---"Name": "aa", "Id": 5
5. 自定义序列化json
Serilog自带了三个json序列化程序:
Serilog.Formatting.Json.JsonFormatter
- 这是Serilog软件包中附带的历史默认值。它生成日志事件的完整呈现,并支持一些配置选项。Serilog.Formatting.Compact.CompactJsonFormatter
- 存在于Serilog.Formatting.Compact
nuget包,提供了更节省空间的 JSON 格式化程序。Serilog.Formatting.Compact.RenderedCompactJsonFormatter
- 同样附带在Serilog.Formatting.Compact
包中,这个格式化程序将消息模板预渲染成文本。
自定义formater:
class User
public int Id get; set;
public string Name get; set;
public DateTime Created get; set;
class CustomDateFormatter : IFormatProvider
readonly IFormatProvider basedOn;
readonly string shortDatePattern;
public CustomDateFormatter(string shortDatePattern, IFormatProvider basedOn)
this.shortDatePattern = shortDatePattern;
this.basedOn = basedOn;
public object GetFormat(Type formatType)
if (formatType == typeof(DateTimeFormatInfo))
var basedOnFormatInfo = (DateTimeFormatInfo)basedOn.GetFormat(formatType);
var dateFormatInfo = (DateTimeFormatInfo)basedOnFormatInfo.Clone();
dateFormatInfo.ShortDatePattern = this.shortDatePattern;
return dateFormatInfo;
return this.basedOn.GetFormat(formatType);
public class Program
public static void Main(string[] args)
var formatter = new CustomDateFormatter("dd-MMM-yyyy", new CultureInfo("en-AU"));
Log.Logger = new LoggerConfiguration()
.WriteTo.Console(formatProvider: new CultureInfo("en-AU")) // Console 1
.WriteTo.Console(formatProvider: formatter) // Console 2
.CreateLogger();
var exampleUser = new User Id = 1, Name = "Adam", Created = DateTime.Now ;
Log.Information("Created @User on Created", exampleUser, DateTime.Now);
Log.CloseAndFlush();
输出:
[13:57:12 INF] Created "Id": 1, "Name": "Adam", "Created": "2020-09-01T13:56:59.7803740-05:00", "$type": "User" on 1/09/2020 1:57:12 PM
[13:57:12 INF] Created "Id": 1, "Name": "Adam", "Created": "2020-09-01T13:56:59.7803740-05:00", "$type": "User" on 01-Sep-2020 1:57:12 PM
参考:
https://github.com/serilog/serilog/wiki/Getting-Started
以上是关于Serilog 2.10 中文文档的主要内容,如果未能解决你的问题,请参考以下文章