.NET Core 开发实战
  • 目录
  • 第1课:课程介绍
  • 第2课:内容综述
  • 第3课:.NET Core的现状、未来以及环境搭建
  • 第4课:Startup:掌握ASP.NET Core的启动过程
  • 第5课:依赖注入:良好架构的起点
  • 第6课:作用域与对象释放行为
  • 第7课:用Autofac增强容器能力
  • 第8课:配置框架:让服务无缝适应各种环境
  • 第9课:命令行配置提供程序
  • 第10课:环境变量配置提供程序
  • 第11课:文件配置提供程序
  • 第12课:配置变更监听
  • 第13课:配置绑定:使用强类型对象承载配置数据
  • 第14课:自定义配置数据源:低成本实现定制化配置方案
  • 第15课:选项框架:服务组件集成配置的最佳实践
  • 第16课:选项数据热更新:让服务感知配置的变化
  • 第17课:为选项数据添加验证:避免错误配置的应用接收用户流量
  • 第18课:日志框架:聊聊记日志的最佳姿势
  • 第19课:日志作用域:解决不同请求之间的日志干扰
  • 第20课:结构化日志组件Serilog:记录对查询分析友好的日志
  • 第21课:中间件:掌控请求处理过程的关键
  • 第22课:异常处理中间件:区分真异常与逻辑异常
  • 第23课:静态文件中间件:前后端分离开发合并部署骚操作
  • 第24课:文件提供程序:让你可以将文件放在任何地方
  • 第25课:路由与终结点:如何规划好你的Web API
  • 第26课:工程结构概览:定义应用分层及依赖关系
  • 第27课:定义Entity:区分领域模型的内在逻辑和外在行为
  • 第28课:工作单元模式(UnitOfWork):管理好你的事务
  • 第29课:定义仓储:使用EF Core实现仓储层
  • 第30课:领域事件:提升业务内聚,实现模块解耦
  • 第31课:APIController:定义API的最佳实践
  • 第32课:集成事件:解决跨微服务的最终一致性
  • 第33课:集成事件:使用RabbitMQ来实现EventBus
  • 第34课:MediatR:轻松实现命令查询职责分离模式(CQRS)
  • 第35课:MediatR:让领域事件处理更加优雅
Powered by GitBook
On this page

第20课:结构化日志组件Serilog:记录对查询分析友好的日志

学习分享 丨作者 / 郑 子 铭 丨公众号 / DotNet NB / CloudNative NB

之前讲解的日志框架,记录的日志都是文本,而且是非结构化的,这样一串串文本实际上不利于我们去做分析

结构化的日志它的好处就显而易见,它可以让我们更易于去检索,更易于与现有的分析系统进行结合

结构化日志的主要场景:

1、实现日志告警

2、实现上下文的关联:可以在日志系统里面对一段业务逻辑输出的日志进行分析

3、实现与追踪系统集成:在调用链的系统里面看到有问题的情况下,可以追踪到调用链过程中间的所有的日志信息

这里创建的依然是一个默认的 ASP.NET Core 的工程

引用包:Serilog.AspNetCore

这个包实际上依赖了 Serilog 很多的内置的包

比如核心的 Serilog (2.8.0)

配置 Serilog.Settings.Configuration (3.1.0)

Console 的输出 Serilog.Sinks.Console (3.1.1)

Debug 的输出 Serilog.Sinks.Debug (1.0.1)

File 的输出 Serilog.Sinks.File (4.0.0)

我们在 Program 这里提前读取一下配置,然后传递给 Serilog 的初始化过程,这里我们把 Main 函数进行了稍微的改造,以让 Serilog 可以接替整个默认的日志记录框架

namespace LoggingSerilogDemo
{
    public class Program
    {
        // 读取配置
        public static IConfiguration Configuration { get; } = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
            .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json", optional: true)
            .AddEnvironmentVariables()
            .Build();

        public static int Main(string[] args)
        {
            // 将配置传递给 Serilog 的初始化过程
            Log.Logger = new LoggerConfiguration().ReadFrom.Configuration(Configuration)
            .MinimumLevel.Debug()
            .Enrich.FromLogContext()
            .WriteTo.Console(new RenderedCompactJsonFormatter())
            .WriteTo.File(formatter: new CompactJsonFormatter(), "logs\\myapp.txt", rollingInterval: RollingInterval.Day)
            .CreateLogger();
            try
            {
                Log.Information("Starting web host");
                CreateHostBuilder(args).Build().Run();
                return 0;
            }
            catch (Exception ex)
            {
                Log.Fatal(ex, "Host terminated unexpectedly");
                return 1;
            }
            finally
            {
                Log.CloseAndFlush();
            }
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                })
                .UseSerilog(dispose: true);// dispose 设置为 true,它就会在退出时帮我们释放我们的日志对象
    }
}

启动程序,输出如下:

{"@t":"2020-03-08T15:47:40.2569100Z","@m":"Starting web host","@i":"4872fd06"}
{"@t":"2020-03-08T15:47:44.1978171Z","@m":"Get 随机创建数据","@i":"6936e72c","SourceContext":"LoggingSerilogDemo.Controllers.WeatherForecastController","ActionId":"8d8ebb60-2211-4acb-bc91-a079be45a689","ActionName":"LoggingSerilogDemo.Controllers.WeatherForecastController.Get (LoggingSerilogDemo)","RequestId":"0HLU3F052RUUN:00000001","RequestPath":"/weatherforecast","SpanId":"|99917a4d-4ccf47636d09b066.","TraceId":"99917a4d-4ccf47636d09b066","ParentId":""}

可以看到每一行都是一个 json,也就是将日志输出为 json 格式,这就意味着可以在整个日志系统里面以 json 的格式去检索数据,比如 SourceContext 就是 Logger 的 name

它还记录了请求上下文,并且输出了 RequestId,SpanId,TraceId,ParentId

RequestId 与 SpanId 的作用就是与追踪系统可以结合

我们记录的日志的方式实际上是与之前是一样的,Controller 里面还是注入了 ILogger,依然使用 ILogger 来记录日志

namespace LoggingSerilogDemo.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class WeatherForecastController : ControllerBase
    {
        private static readonly string[] Summaries = new[]
        {
            "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
        };

        private readonly ILogger<WeatherForecastController> _logger;

        public WeatherForecastController(ILogger<WeatherForecastController> logger)
        {
            _logger = logger;
        }

        [HttpGet]
        public IEnumerable<WeatherForecast> Get()
        {

            _logger.LogInformation("Get 随机创建数据");
            var rng = new Random();
            return Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = DateTime.Now.AddDays(index),
                TemperatureC = rng.Next(-20, 55),
                Summary = Summaries[rng.Next(Summaries.Length)]
            })
            .ToArray();

        }
    }
}

也就是说可以通过简单的配置和几行代码的设置就可以替换官方提供的日志框架,让我们具备记录结构化日志的能力

我们刚才看到日志输出到 Console,同时输出到文件,可以看到 logs 目录已经产生了一个 myapp20200308.txt 文件

{"@t":"2020-03-08T15:47:40.2569100Z","@mt":"Starting web host"}
{"@t":"2020-03-08T15:47:44.1978171Z","@mt":"Get 随机创建数据","SourceContext":"LoggingSerilogDemo.Controllers.WeatherForecastController","ActionId":"8d8ebb60-2211-4acb-bc91-a079be45a689","ActionName":"LoggingSerilogDemo.Controllers.WeatherForecastController.Get (LoggingSerilogDemo)","RequestId":"0HLU3F052RUUN:00000001","RequestPath":"/weatherforecast","SpanId":"|99917a4d-4ccf47636d09b066.","TraceId":"99917a4d-4ccf47636d09b066","ParentId":""}

这个文件可以看到每一行是一条日志,每一条日志都是一个 json 对象,包括刚才我们记录的 Get 随机创建数据,已经输出出来了

我们可以调整日志级别,打开配置文件

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "Serilog": {
    "MinimumLevel": {
      "Default": "Information",
      "Override": {
        "Microsoft": "Error",
        "System": "Information"
      }
    }
  },
  "AllowedHosts": "*"
}

Serilog 需要单独配置,它与之前的配置方式略有不同,它需要配置最小的日志输出级别,默认是 Information

Override 是重载上面 Logging 定义的日志级别

设置 Microsoft 为 Error 之后会把 Microsoft 默认的日志输出级别过滤掉

也意味着整个的配置和输出的方式与之前是级别类似的,我们可以把日志输出到 Console,也可以把日志输出到文件,当然实际上 Serilog 还提供了很多的这种输出的提供程序,还可以与 EFK,ELK 这种日志的套件进行集成,把日志输出到分析系统里面

Previous第19课:日志作用域:解决不同请求之间的日志干扰Next第21课:中间件:掌控请求处理过程的关键

Last updated 3 years ago

GitHub源码链接:

https://github.com/MingsonZheng/DotNetCoreDevelopmentActualCombat/tree/main/LoggingSerilogDemo