.NET 日志框架
我们平时的开发离不开记录日志,.net core框架也内置了强大的日志记录功能。
简单示例
创建一个控制台应用,在 appsettings.json
中加入如下配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
"Logging": {
"LogLevel": {
"Default": "Debug",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
},
"Console": {
"IncludeScopes": true,
"LogLevel": {
"Default": "Debug",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information",
"alog": "Debug"
}
}
}
引用包
1
2
Microsoft.Extensions.Logging
Microsoft.Extensions.Logging.Console
注入 Logging
到容器中
1
2
3
4
5
6
7
8
9
10
IConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
configurationBuilder.AddJsonFile("appsettings.json", false, false);
var configuration = configurationBuilder.Build();
var services = new ServiceCollection();
services.AddLogging(builder =>
{
builder.AddConfiguration(iconfigurationRoot.GetSection("Logging"));
builder.AddConsole();
});
var serviceProvider = services.BuildServiceProvider();
下面是 AddLogging
的源码,其中主要的部分就是将 ILoggerFactory
和 ILogger<>
注入到容器中。
1
2
3
4
5
6
7
8
9
10
11
12
13
public static IServiceCollection AddLogging(this IServiceCollection services, Action<ILoggingBuilder> configure)
{
if (services == null)
{
throw new ArgumentNullException("services");
}
services.AddOptions();
services.TryAdd(ServiceDescriptor.Singleton<ILoggerFactory, LoggerFactory>());
services.TryAdd(ServiceDescriptor.Singleton(typeof(ILogger<>), typeof(Logger<>)));
services.TryAddEnumerable(ServiceDescriptor.Singleton((IConfigureOptions<LoggerFilterOptions>)new DefaultLoggerLevelConfigureOptions(LogLevel.Information)));
configure(new LoggingBuilder(services));
return services;
}
我们可以通过两种方式创建ILogger对象
1
2
3
4
5
6
7
var logFactory = serviceProvider.GetService<ILoggerFactory>();
var logger = logFactory.CreateLogger("Default");
//或
var logger = serviceProvider.GetService<ILogger<Program>>();
logger.LogInformation("aa");
logger.LogError("bb");
logger.LogDebug("cc");
这样我们就完成了打印日志的简单示例。
日志级别
日志级别分为如下7种,从上往下日志级别由低到高。如在配置文件中设置日志级别为 Error
,那么 Warning
等低级别的日志都不会被打印。
1
2
3
4
5
6
7
8
9
10
public enum LogLevel
{
Trace,
Debug,
Information,
Warning,
Error,
Critical,
None
}
打印日志时推荐使用字符串模板的方式
1
logger.LogDebug("时间:{date}",DateTime.Now);
不推荐下面这种方式,因为我们调试时可能记录大量 Debug
级别日志,当项目投入生产后,我们往往会关闭 Debug
级别日志,字符串模板方式在记录日志时才会进行字符串拼接,所以字符串模板的方式可以防止 Dubug
级别日志的字符串拼接影响性能。
1
logger.LogDebug($"时间:{DateTime.Now}");
日志域
使用日志域方法,可以让同一作用域下的日志带上相同的 scopeId
,这样可以帮助我们更好的做日志追踪和排查问题。
在 Console
配置文件中添加如下配置
1
"IncludeScopes": true
创建日志域并记录日志
1
2
3
4
5
6
using (logger.BeginScope("scopeId:{scopeId}", Guid.NewGuid()))
{
logger.LogInformation($"时间:{DateTime.Now}");
logger.LogError("bb");
logger.LogDebug("cc");
}
Serilog框架记录结构化日志
结构化日志相比与文本日志易于检索,易于分析统计,可以用于日志告警,日志关联,以及与追踪系统的集成。下面演示 Serilog
的简单使用实例。
创建一个 .net core web
应用项目,并引用下面的nuget包
1
Serilog.AspNetCore
在 appsettings.json
中添加如下配置
1
2
3
4
5
6
7
8
9
"Serilog": {
"MinimumLevel": {
"Default": "Debug",
"Override": {
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
在 Program.cs
中添加如下代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Program
{
public static void Main(string[] args)
{
var configuration = new ConfigurationBuilder().AddJsonFile("appsettings.json", false, true).Build();
Log.Logger = new LoggerConfiguration().ReadFrom.Configuration(configuration)
.WriteTo.Console(new RenderedCompactJsonFormatter())
.Enrich.FromLogContext()
.CreateLogger();
Log.Logger.Information("程序启动 {date}",DateTime.Now);
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
}).UseSerilog(dispose:true);
}
在控制器中引用 ILogger<>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class WeatherForecastController : ControllerBase
{
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[HttpGet]
public string Get()
{
_logger.LogInformation("hello serilog ");
return "OK";
}
}
这样我们打印的日志就都是以Json结构记录的了。
上面的日志记录的较多,我们可以先将无用的日志关闭,在配置文件中将 Microsoft.Hosting.Lifetime
的日志级别设置成 Fatal
,这样我们的日志就清晰多了。
Serilog
日志框架还支持多种的 Sinks
接收器,可以通过引用 Serilog.Sinks.Http
或 Serilog.Sinks.Elasticsearch
将日志记录到 ELK
中。