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