我们平时的开发离不开记录日志,.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

net es日志只有_id没有内容_json

 通过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结构记录的。

net es日志只有_id没有内容_json_02

   上面的日志记录的较多,我们可以先将无用的日志关闭,在配置文件中将 Microsoft.Hosting.Lifetime的日志级别设置成 “Fatal”,这样我们的日志就清晰多了。

net es日志只有_id没有内容_json_03

   Serilog日志框架还支持多种的Sinks接收器,可以通过引用 Serilog.Sinks.Http 或 Serilog.Sinks.Elasticsearch 将日志记录到 ELK中。