.NET项目重构之日志服务(Serilog)

发布时间 2023-07-25 17:13:37作者: HOYU_Z

1. 目录

2. 前言

定时任务中比较重要的一个环节就是日志记录,有了日志可以记录系统的操作过程,也可以在系统异常时方便排查错误原因。

比如定时任务经常要做的一个事情,同步其它异构系统数据到本系统。大多情况下的操作过程时,定时请求对方接口返回JSON格式数据,我方把接收到的数据再转换为C#对象进行业务处理后保存至数据库。如果在这个过程中发生系统异常,没有日志记录很有可能连复现BUG都很难做到。

所以在这个过程中,比较正确的日志记录方式是:

  1. 接口请求,如有异常记录日志。
  2. 记录接口返回值。
  3. JSON数据转换为C#对象,如有异常记录日志。
  4. 其它

2.1. 日志控件的选择

之前所有项目用的基本都是老牌Log4net,而这次重构的一大主题是学习新东西嘛,所以我选择比较年轻一点的Serilog。

为什么选择Serilog? 嗯。。。。随便选的,其实用NLog也不是不行。

3. 日志配置

在Web项目下创建Extensions文件夹并添加SerilogSetup.cs文件。

添加如下NuGet包

  • Serilog 3.0.1 基础包
  • Serilog.Extensions.Hosting 7.0.0 ConfigureHostBuilder的扩展包
  • Serilog.Enrichers.Thread 3.1.0 用于获取线程信息
  • Serilog.Sinks.Async 1.5.0 用于异步打印
  • Serilog.Sinks.Console 4.1.0 打印日志到控制台
  • Serilog.Sinks.File 5.0.0 打印日志到文件

添加全局静态变量,用于设置打印模板,至于写法。官网是最好的老师:https://github.com/serilog/serilog/wiki/Configuration-Basics#output-templates

    private static readonly string logTemplate = "{NewLine}时闻:{Timestamp:yyyy-MM-dd HH:mm:ss.fff}{NewLine}日志等级:{Level}{NewLine}线程ID:{ThreadId} 线程名:{ThreadName}{NewLine}所在类:{SourceContext}{NewLine}日志信息:{Message}{NewLine}{Exception}";

3.1. 控制台打印

接着添加静态方法AddSerilog

    public static void AddSerilog(ConfigureHostBuilder builder)
    {
         // 打印到控制台
         // 生产环境中可以不向控制台打印
         builder.UseSerilog((context, logger) =>
         {
             // 添加此句后可以在“执行上下文”中动态添加和删除属性
             logger.Enrich.FromLogContext();

             logger.MinimumLevel.Information();
             logger.Enrich.WithThreadId();
             logger.Enrich.WithThreadName();

             logger.WriteTo.Logger(lg => lg.WriteTo.Async(a =>
                     a.Console(outputTemplate: logTemplate)));
         });
    }

3.2. 文件打印

在AddSerilog方法中添加如下内容

   // 打印到文件
   builder.UseSerilog((context, logger) =>
   {
       logger.Enrich.FromLogContext();
       logger.Enrich.WithThreadId();
       logger.Enrich.WithThreadName();

       logger.WriteTo.Logger(lg => lg.Filter.ByIncludingOnly(p => p.Level == LogEventLevel.Debug).WriteTo.Async(a => a.File(logDebug, rollingInterval: RollingInterval.Hour, outputTemplate: logTemplate)));
       logger.WriteTo.Logger(lg => lg.Filter.ByIncludingOnly(p => p.Level == LogEventLevel.Information).WriteTo.Async(a => a.File(logInfo, rollingInterval: RollingInterval.Hour, outputTemplate: logTemplate)));
       logger.WriteTo.Logger(lg => lg.Filter.ByIncludingOnly(p => p.Level == LogEventLevel.Warning).WriteTo.Async(a => a.File(logWarn, rollingInterval: RollingInterval.Hour, outputTemplate: logTemplate)));
       logger.WriteTo.Logger(lg => lg.Filter.ByIncludingOnly(p => p.Level == LogEventLevel.Error).WriteTo.Async(a => a.File(logError, rollingInterval: RollingInterval.Hour, outputTemplate: logTemplate)));
       logger.WriteTo.Logger(lg => lg.Filter.ByIncludingOnly(p => p.Level == LogEventLevel.Fatal).WriteTo.Async(a => a.File(logFatal, rollingInterval: RollingInterval.Hour, outputTemplate: logTemplate)));

   });

这里要注意的是,也可以写在一个方法内,我这样做的目的是为了可以一目了然的区分开不同日志的配置。

最后在Program.cs 中注册Serilog相关配置。

SerilogSetup.AddSerilog(builder.Host);

接下来就可以找个地方做打印测试了,比如在HomeController的Index的方法中加放入

    _logger.LogInformation("控制台打印");

4. 结语

目前阶段只是做了控制台和文件打印,以后也许会接入数据库打印。

因为后期想在Web页面查看定时任务的执行状况等等。