最近使用Asp.net Core进行项目开发,日志功能自然是必不可少的。现在使用Asp.net Core 2.2 版,它自带的Logger虽然好用,但是不支持保存到日志文件,所以一番搜索后决定使用Serilog这个第三方Log Provider。它的好处不多说,这里简单记录一下我自己的操作。
为什么要从配置文件里设置Serilog
因为这个项目最终是发布给客户的,C#源代码会隐藏,而项目的Log功能又要求能灵活配置,例如客户想指定不同的输出目标(Console,File)以及不同的Log 级别。此时Serilog官方默认的代码生成方式就不合适,因为它无法实现灵活配置。下面代码就固定了将Log只输出到Console而无法再更改:
我的解决办法
综合了Serilog的官方站点以及几个网页,最终总结如下。
1. 安装Package
使用Nuget package manger或者命令行方式添加如下Package,有些是可选的,看你自己是否需要用到。
- Serilog.AspNetCore 必须
- Serilog.Settings.Configuration 必须, 提供从配置文件读取Serilog的支持
- Serilog.Sinks.Console 提供输出到Console的支持
- Serilog.Sinks.File 提供输出到文件的支持
- Serilog.Sinks.Async 提供异步输出log的支持
- Serilog.Enrichers.Environment 提供Enricher里的MachiName支持
- Serilog.Enrichers.Thread 提供Enricher里的ThreadId支持
2. 修改Program.cs生成Logger对象
我们按照习惯,会把Serilog相关的日志配置放置在appsetting.jso这个文件,当然,也可以放在根据开发环境不同而区分开的子配置文件里,比如appsettings.Development.json文件,因此Main()函数里添加如下语句:
var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{environment}.json", optional: true, reloadOnChange: true)
.Build();
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(config)
.CreateLogger();
上面第一行的environment变量是为了支持读取从子配置文件而设置,可以看到上面的代码块实现了一个通用的对象生成,并没有进行任何Serilog相关的配置,符合我们要求。
然后在Build的时候添加一句useSerilog():
到此为止,Logger对象生成完毕。
3. 使用Logger对象
它的后续使用就和AspNet Core 自带的logger用法一样了。可以在Controller里通过依赖注入的方式生成1个Logger对象。采用Serilog的好处就是这段代码和采用自带Logger的代码完全一样,不需要任何修改,具体见下图:
上面6个函数对应自带Logger的6个等级,而Serilog 也相应地有对应的6个等级,只是有两个的名字略有差别,Trace -> Verbose, Critical -> Fatal,上述代码执行后,输出到Console的Log截图可以看出它们名字差别:
4. 详解配置文件
下面是配置文件的详细解释,首先把整个文件关于Serilog的配置放置在此:
{
"Serilog": {
"Using": [ "Serilog.Sinks.File", "Serilog.Sinks.Async", "Serilog.Sinks.Console" ],
"MinimumLevel": {
"Default": "Verbose",
"Override": {
"Microsoft": "Warning"
}
},
"WriteTo": [
{
"Name": "Async",
"Args": {
"configure": [
{
"Name": "File",
"Args": {
"path": "log.txt",
"rollingInterval": "Day",
"restrictedToMinimumLevel": "Verbose"
}
}
]
}
},
{
"Name": "Console",
"Args": {
"restrictedToMinimumLevel": "Verbose",
"outputTemplate": "{Timestamp:HH:mm:ss.fff zzz} [{Level}] {Message} {NewLine}{Exception}"
}
}
],
"Enrich": [ "WithMachineName", "WithThreadId" ],
"Properties": {
"Application": "MyApplicationName"
}
}
}
这里我综合了如下几个网页:
https://github.com/serilog/serilog-settings-configuration
https://github.com/serilog/serilog/wiki/Configuration-Basics
重点解释一下下面几行:
- 第2行列出要使用的Sink,这里我用了3个,Console, File, Async
- 第3行开始的MinimumLevel是Global设置,会影响所有Sink的允许输出的Log level,因此它要设置为各Sink Level的最小值。
- 第10行开始的WriteTo 列出所有Sink的具体配置,它是一个Json Array。
- 第18行开始的File文件属性,path指定文件路径及扩展名,rollingInterval 是自动生成log文件的格式,此配置下,每天会在当前路径下创建1个log文件,名字为logyyyymmdd.txt. restrictMinimumLevel 是Log输出等级,注意最低是Verbose,而不是用默认的Trace
- Console里面示例了指定输出字符模板样式,可以任意添加修改各种字符。
- 第38行的Enrich只出了Enricher需要的几个域,如果想添加这些域到每条Log message,则可以如下操作:
"outputTemplate": "ZZ==> {Timestamp:HH:mm:ss.fff zzz} [{Level}] {Message} [TheadId={ThreadId}] \"{MachineName}\" {NewLine}{Exception}"
上面代码就是访问了TrheadId和MachiName,输出的Log格式如下:
ZZ==> 14:38:55.426 -06:00 [Warning] _logger.LogWarning [TheadId=10] "DESKTOP-TEST"
5. 默认输出模板
另外官方文档对默认的输出模板里的几个参数含义也做了解释:
"{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}"
-
{Level:u3}
or{Level:w3}
for three-character upper- or lowercase level names, respectively.
2019-07-22 14:38:24.556 -06:00 [DBG] _logger.LogDebug
2019-07-22 14:38:24.562 -06:00 [INF] _logger.LogInformation
2019-07-22 14:38:24.610 -06:00 [WRN] _logger.LogWarning -
{Message:lj}
format options cause data embedded in the message to be output in JSON (j
)