Sink 接收器模块、输出方式、接收模块库、输出模块库
Diagnostic 诊断
Enricher 扩展器
embedded 嵌入式的
compact 紧凑的、简洁的
concept 概念
usage 用法
restrict 限制、约束
raise 提升
necessary 必要的
digging 挖掘
Serilog.AspNetCore
日志包主体
Serilog.AspNetCore.RollingFile
将日志写入文件
appsettings.json
中添加 Serilog
节点。简单说明下配置文件的意思:
将日志写入RollingFile(文件)和Console(控制台)。
RollingFile
的具体配置:记录文件到 根目录/logs/{日期}.txt
文件内,每天记录一个文件,并且只记录 Warning
及其以上的日志;
默认日志级别记录 Debug
及其以上的日志。
如果日志包含 Microsoft
System
,只记录级别为 Information
及以上的日志。
{
"Serilog": {
"WriteTo": [
{
"Name": "RollingFile",
"Args": {
"pathFormat": "logs\{Date}.txt",
"RestrictedToMinimumLevel": "Warning"
}
},
{
"Name": "Console"
}
],
"MinimumLevel": {
"Default": "Debug",
"Override": {
"Microsoft": "Information",
"System": "Information"
}
}
},
}
program.cs
,注册 Serilog
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
}).UseSerilog((context, configure) =>
{
configure.ReadFrom.Configuration(context.Configuration);
});
Serilog
了。private readonly ILogger<HomeController> _logger;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
public IActionResult Index()
{
_logger.LogError("Error 测试");
return View();
}
接收器用来配置日志记录到哪种介质。比如说 Console(输出到控制台),File(日志文件)等。就是我们上面配置的 WriteTo
节点。
接收器一般以扩展库的方式提供的,为了将日志写入文件,我们在上面引入了 Serilog.AspNetCore.Sinks.RollingFile
包。
WriteTo
指定,也可以通过代码配置,比如在 Console
控制台程序中使用:Log.Logger = new LoggerConfiguration()
.WriteTo.Console()
.CreateLogger();
Log.Information("Ah, there you are!");
Log.Logger = new LoggerConfiguration()
.WriteTo.Console()
.WriteTo.File("log-.txt", rollingInterval: RollingInterval.Day)
.CreateLogger();
output templates
来控制格式。Log.Logger = new LoggerConfiguration()
.WriteTo.File("log.txt",
outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}")
格式这部分涉及到扩展器,会在后面具体说明,这里只是简单提一下如何使用。
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.File("log.txt")
.WriteTo.Console(restrictedToMinimumLevel: LogEventLevel.Information)
.CreateLogger();
上面代码中指定了默认日志级别为 Debug
,但为 Console Sink
的日志重写级别为 Information
==========
在声明Logger时可以通过 MinimumLevel.Debug()
指定最小级别,而在 WriteTo
中指定接收器Sink时,也可以通过 restrictetToMinimumLevel:LogEventLevel.Information
指定最小级别,那两者之间的关系是怎么样的呢?
注意:接收器Sink的级别必须高于Logger的级别。 假设Logger的默认级别为 Information
,即便 Sink
重写日志级别为 LogEventLevel.Debug
,也只能看到 Information
及以上级别的日志。这是因为默认级别的配置控制哪些事件级别的日志记录语句可以被创建,而 Sink
级别仅仅是对这些事件进行过滤。
顾名思义,扩展器可以添加,删除或修改附加到日志事件的属性。 例如,下面的代码可以达到将线程ID附加到每个事件的目的。
class ThreadIdEnricher : ILogEventEnricher
{
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
{
logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty(
"ThreadId", Thread.CurrentThread.ManagedThreadId));
}
}
使用 Enricher
将扩展添加到配置对象
Log.Logger = new LoggerConfiguration()
.Enrich.With(new ThreadIdEnricher())
.WriteTo.Console(
outputTemplate: "{Timestamp:HH:mm} [{Level}] ({ThreadId}) {Message}{NewLine}{Exception}")
.CreateLogger();
注意模板中的 {ThreadId}
,在日志中打印的便是当前线程的ID了
如果扩展的属性值在整个应用程序运行期间都是恒定的,则可以使用快捷方式WithProperty方法简化配置。
Log.Logger = new LoggerConfiguration()
.Enrich.WithProperty("Version", "1.0.0")
.WriteTo.Console()
.CreateLogger();
可以通过过滤器有选择的记录事件。过滤器只是 LogEvent
的谓词,其中一些常见的情况由 Matching
类处理。
只要在调试过程中打个断点,就可以看到 LogEvent
的详细属性,这里就不赘述,懒~~~
var log = new LoggerConfiguration()
.WriteTo.Console(
outputTemplate:
"{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3} {Count} {Message:j} {NewLine}")
.Filter.ByExcluding(Matching.WithProperty<int>("Count", p => p < 10))
.CreateLogger();
log.Information("测试 {Count}", 20);
log.Information("测试 {Count}", 3);
log.Information("测试 {Count}", 11);
Console.Read();
// 输出:
// 2020-05-05 00:04:46 INF 20 测试 20
// 2020-05-05 00:04:47 INF 11 测试 11
代码说明:忽略参数 Count
小于 10 的日志,所以最终只输出了 2 条。
一个基本的OutTemplate模板大概长这样:
{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj} {NewLine} {Exception}
从头到尾分别是:日期、日志级别、日志内容、换行、异常信息
Serilog的日志是由LogEvent构成的,其可用的参数对应了LogEvent的public属性,分别是下面几个:
serilog 的输出很有意思,并不仅仅能输出字符串,还可以输出对象或枚举。
这里主要以【Message】为说明对象,事实上也有【Properties】用法也差不多,但输出样式稍微有些区别
请看下面的代码:
var user = new User { Id = 1, Name = "张三" };
log.Debug("当前用户: {User},当前时间:{CurrentTime}", user, DateTime.Now)
上面这些类型,表达的内容基本上不会有歧义,所以一般处理方式就是直接调用 .ToString()
方法
var count = 100;
log.Debug("检索到{Count}条记录", count); // 检索到100条记录
其他类型都差不多,大家可以自己测试。
如果属性值传递的IEnumerable
,Serilog会将其视为一个集合
var fruits = new[] {"apple", "pear", "orange"};
log.Information("我有这些水果:{fruit}", fruits); // 我有这些水果:["apple", "pear", "orange"]
var fruits2 = new Dictionary<string,int> {{ "Apple", 1}, { "Pear", 5 }};
log.Information("In my bowl I have {Fruit}", fruits2); // In my bowl I have {"Apple": 1, "Pear": 5}
可以看出,Serilog很智能,根据不同的集合类型输出了不同格式,而该格式正好可以表达数据的内容
var user = new User() {Id = 1, Name = "张三"};
log.Information("{User}", user); // "SerilogSample.User"
log.Information("{@User}", user); // {"Id": 1, "Name": "张三", "$type": "User"}
log.Information("{$User}", user); // "SerilogSample.User"
默认情况下,如果Serilog没能识别数据类型,直接调用 ToString()
,对应实例 1
上面那种情况,我们一般希望保留对象的结构,对此,Serilog提供了 @
符号,叫做 析构运算符。添加该符号后,默认输出对象的JSON格式,对应实例 2
强制字符串化,通过 $
符号,可以强制调用对应参数的 ToString()
方法,对应实例3
补充下上面一条,如果 log.Information("{$User}", "abc")
输出啥呢?很简单,"abc".ToString()
所以输出内容就是 abc 嘛
自定义数据,注意代码的第二行,Serilog提供了很多不同的析构策略,大家自己试吧。注意:ByTransforming 必须返回与输入参数不同的类型,不然会被递归调用
var log = new LoggerConfiguration()
.Destructure.ByTransforming
.WriteTo.Console(outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3} {Message:j} {NewLine}")
.CreateLogger();
var user = new User() {Id = 1, Name = "张三"};
log.Information("{@User:l}", user); // {"UserName": "张三"}
var log = new LoggerConfiguration()
.WriteTo.Console(
outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss} {User} {EventType} {Level:u3} {Message} {Exception} {NewLine}")
.Enrich.WithProperty("EventType", "事件类型")
.Enrich.WithProperty("User", new User { Id = 1, Name = "测试" })
.CreateLogger();
var exception = new Exception("人工异常");
log.Error(exception, "啥也不是");
格式说明:
Timestamp:时间戳,指定格式:{Timestamp: yyyy-MM-dd HH:mm:ss.fff zzz}
Level:日志级别,默认为完整的级别名称,如:Information
。可选格式 :u3 (日志级别缩写,三个字母,大写)和 :w3 (日志级别缩写,三个字母,小写),效果:{Level:u3}=INF;{Level:w3}=inf
Properties:命名属性,除了上面提到过的5个参数(Timestamp,Exception,Message,NewLine,Level)外的所有其他参数。这些参数来源于:
var log = new LoggerConfiguration()
.WriteTo.Console(
outputTemplate:
"{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3} {User:lj} {Message:j} {NewLine}")
.Filter.ByExcluding(Matching.WithProperty
.Enrich.WithProperty("User", "Enricher")
.CreateLogger();
log.Information("{User}", "Message");
// 输出:2020-05-05 00:19:39 INF Message "Message"
虽然在Enricher中指定了属性User,但最终输出的是Message模板指定的实参。修改一下代码:
var log = new LoggerConfiguration()
.WriteTo.Console(
outputTemplate:
"{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3} {User:lj} {Message:j} {NewLine}")
.Filter.ByExcluding(Matching.WithProperty<int>("Count", p => p < 10))
.Enrich.WithProperty("User", "Enricher")
.CreateLogger();
log.Information("{User1}", "Message");
// 输出:2020-05-05 00:19:39 INF Enricher "Message"
Message: 消息内容;如果要输出对象的JSON,一般会指定格式参数 {Message:lj}
;
参数 l
的作用:
var log = new LoggerConfiguration()
.WriteTo.Console(
outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3} {Message:l}")
.CreateLogger();
log.Information("{a} {b}", 23, "abc");
// 输出为:2020-05-04 23:41:12 INF 23 abc
var log = new LoggerConfiguration()
.WriteTo.Console(
outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3} {Message}")
.CreateLogger();
log.Information("{a} {b}", 23, "abc");
// 输出为:2020-05-04 23:44:21 INF 23 "abc"
注意输出的 abc
字符串,添加了参数 :l
后,若格式的实参是字符串类型,会自动删除双引号。
如果要输出一个对象,应该怎么做呢?看下面的三种做法:
var log = new LoggerConfiguration()
.WriteTo.Console(
outputTemplate:
"{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3} {Message}")
.CreateLogger();
var user = new User {Id = 1, Name = "张三"};
log.Information("{u}", user);
// 输出为:2020-05-04 23:47:08 INF "SerilogSample.User"
可以看到,输出的是对象类型的名称,修改代码如下:
var log = new LoggerConfiguration()
.WriteTo.Console(
outputTemplate:
"{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3} {Message}")
.CreateLogger();
var user = new User {Id = 1, Name = "张三"};
log.Information("{@u}", user);
// 输出为:2020-05-04 23:49:59 INF User {Id=1, Name="张三"}
添加参数 :j
,修改代码如下:
var log = new LoggerConfiguration()
.WriteTo.Console(
outputTemplate:
"{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3} {Message:j}")
.CreateLogger();
var user = new User {Id = 1, Name = "张三"};
log.Information("{@u}", user);
// 输出为:2020-05-04 23:51:42 INF {"Id": 1, "Name": "张三", "$type": "User"}
由上面的测试可以看出,
{Message:j}
{Message:lj}
就够了修改下 Program.cs
文件
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureAppConfiguration(configure => configure.AddJsonFile("Serilog.json"));
webBuilder.UseStartup<Startup>();
})
.UseSerilog((context, configure) =>
{
configure.MinimumLevel.Information()
.WriteTo.Console(restrictedToMinimumLevel: LogEventLevel.Debug)
.Filter.With<MicrosoftFilter>()
.WriteTo.RollingFile("logs\\{Date}.txt", LogEventLevel.Warning);
});
自定义一个 Filter
public class MicrosoftFilter : ILogEventFilter
{
public bool IsEnabled(LogEvent logEvent)
{
if (!logEvent.Properties.TryGetValue("SourceContext", out var source))
{
return logEvent.Level >= LogEventLevel.Debug;
}
if (source.ToString().StartsWith("\"Microsoft"))
{
return logEvent.Level >= LogEventLevel.Warning;
}
return logEvent.Level >= LogEventLevel.Debug;
}
}
差不多就这样吧~~告辞!!!
手机扫一扫
移动阅读更方便
你可能感兴趣的文章