第三十八节:SeriLog日志的各种用法以及与Core MVC封装集成

一. 简介

1. 说明

 Serilog是一个用于.net应用程序的诊断日志库。它易于设置,拥有简介的API,并且可以在所有最新的.net平台上运行。虽然它甚至在最简单的应用程序中也很有用,但Serilog对结构化日志记录的支持在检测复杂、分布式和异步的应用程序和系统时非常出色。 和其它日志类库一样,Serilog可以输出到控制台、文件、数据库等多种载体。

2. 各种输出载体

 下面是各种载体对应的程序集(很多,下面仅列出部分,可去Nuget上查找),本节重点介绍:控制台、各种文件(同步和异步)、SQLServer的写入。

 【Serilog.Sinks.Console】

 【Serilog.Sinks.File】

 【Serilog.Sinks.Async】 异步写入文件,需要配合File程序集一块使用

 【Serilog.Sinks.MSSqlServer】

 【Serilog.Sinks.MySQL】

 【Serilog.Sinks.Seq】

 【Serilog.Sinks.Elasticsearch】

-----下面是Core Mvc专用的,内含多个程序集

 【Serilog.AspNetCore】

3. 参考地址

 (1).GitHub: https://github.com/serilog/serilog            https://github.com/serilog/serilog-aspnetcore

 (2).官网:https://serilog.net/

 (3).参考博客:https://www.cnblogs.com/RainFate/p/12073662.html

                        https://www.cnblogs.com/diwu0510/p/12828519.html

二. 各种实操测试

1. 前言

(1). 日志级别

 FATAL(致命错误) > ERROR(一般错误) > Warning(警告) > Information(一般信息) > DEBUG(调试信息)>Verbose(详细模式,即全部)

 代码中通过MinimumLevel.Debug()来设置日志级别,设置后只能捕获该级别和高于该级别的日志信息了

(2). 日志模板

 框架定义的参数有:LogLevel日志级别、Message日志信息、Exception异常信息、NewLine换行,可以自定义格式如下:

 "{NewLine}Date:{Timestamp:yyyy-MM-dd HH:mm:ss.fff}{NewLine}LogLevel:{Level}{NewLine}Message:{Message}{NewLine}{Exception}" + new string('-', 100);

2. 输出控制台

(1). 通过Nuget安装程序集:【Serilog 2.10】【Serilog.Sinks.Console 3.1.1】

(2). 设置日志捕获级别、日志模板、输出途径(WriteTo.Console),并且createLogger即可

(3). 通过各种方法进行调用

注: Log.CloseAndFlush();就销毁了该对象,再次使用需要重新创建了。

代码分析:

            {
                string SerilogOutputTemplate = "{NewLine}Date:{Timestamp:yyyy-MM-dd HH:mm:ss.fff}{NewLine}LogLevel:{Level}{NewLine}Message:{Message}{NewLine}{Exception}" + new string('-', 100);
                Log.Logger = new LoggerConfiguration()
                            .MinimumLevel.Debug() // 捕获的最小日志级别
                            .WriteTo.Console(LogEventLevel.Verbose, SerilogOutputTemplate)
                            .CreateLogger();
                Log.Information("123456789");
                try
                {
                    int.Parse("sfsdfsf");
                }
                catch (Exception ex)
                {
                    Log.Error(ex, ex.Message);
                }
                Log.CloseAndFlush();
            }

3. 输入文件-同步

(1). 通过Nuget安装程序集:【Serilog 2.10】【Serilog.Sinks.File 4.1.0】

(2). 设置日志捕获级别、日志模板、输出途径(WriteTo.File),并且createLogger即可

(3). 通过各种方法进行调用

PS:WriteTo.File详解

 path:默认路径是程序的bin目录+path参数,当然也可以写绝对路径,只需要写入参数就可以了

 rollingInterval:创建文件的类别,可以是分钟,小时,天,月。 此参数可以让创建的log文件名 + 时间。例如ApiLog20210109.log

 outputTemplate:日志模板,可以自定义

 retainedFileCountLimit:设置日志文件个数最大值,默认31,意思就是只保留最近的31个日志文件,等于null时永远保留文件。

代码分享:

            {
                string SerilogOutputTemplate = "{NewLine}Date:{Timestamp:yyyy-MM-dd HH:mm:ss.fff}{NewLine}LogLevel:{Level}{NewLine}Message:{Message}{NewLine}{Exception}" + new string('-', 100);
                Log.Logger = new LoggerConfiguration()
                            .MinimumLevel.Debug() // 捕获的最小日志级别
                            .WriteTo.File("MyLogs\ApiLog.txt", rollingInterval: RollingInterval.Day, outputTemplate: SerilogOutputTemplate, retainedFileCountLimit: null)
                            .CreateLogger();
                Log.Information("123456789");
                try
                {
                    int.Parse("sfsdfsf");
                }
                catch (Exception ex)
                {
                    Log.Error(ex, ex.Message);

                }
                Log.CloseAndFlush();
            }
View Code

日志文件内容:

4. 输入文件-异步(推荐)

(1). 通过Nuget安装程序集:【Serilog 2.10】【Serilog.Sinks.File 4.1.0】【Serilog.Sinks.Async 1.4.0】

(2). 设置日志捕获级别、日志模板、输出途径(WriteTo.Async(a => a.File())),并且createLogger即可

(3). 通过各种方法进行调用

代码分享:

            {
                string SerilogOutputTemplate = "{NewLine}Date:{Timestamp:yyyy-MM-dd HH:mm:ss.fff}{NewLine}LogLevel:{Level}{NewLine}Message:{Message}{NewLine}{Exception}" + new string('-', 100);
                Log.Logger = new LoggerConfiguration()
                            .MinimumLevel.Debug() // 捕获的最小日志级别
                            .WriteTo.Async(a => a.File("My_Logs\ApiLog.txt", rollingInterval: RollingInterval.Day, outputTemplate: SerilogOutputTemplate, retainedFileCountLimit: null))
                            .CreateLogger();
                Log.Information("123456789");
                try
                {
                    int.Parse("sfsdfsf");
                }
                catch (Exception ex)
                {
                    Log.Error(ex, ex.Message);
                }
                Log.CloseAndFlush();
            }
View Code

5. 按照日志级别存放到不同文件夹-(同步、异步)

(1). 通过Nuget安装程序集:【Serilog 2.10】【Serilog.Sinks.File 4.1.0】【Serilog.Sinks.Async 1.4.0】

(2). 通过Filter设置日志级别,其它类似

(3). 通过各种方法进行调用

代码分享:

            {
                string LogFilePath(string LogEvent) => $@"ALL_Logs{LogEvent}log.log";
                string SerilogOutputTemplate = "{NewLine}Date:{Timestamp:yyyy-MM-dd HH:mm:ss.fff}{NewLine}LogLevel:{Level}{NewLine}Message:{Message}{NewLine}{Exception}" + new string('-', 100);
                //同步写法
                //Log.Logger = new LoggerConfiguration()
                //            .Enrich.FromLogContext()
                //            .MinimumLevel.Debug() // 所有Sink的最小记录级别
                //            .WriteTo.Logger(lg => lg.Filter.ByIncludingOnly(p => p.Level == LogEventLevel.Debug).WriteTo.File(LogFilePath("Debug"), rollingInterval: RollingInterval.Day, outputTemplate: SerilogOutputTemplate))
                //            .WriteTo.Logger(lg => lg.Filter.ByIncludingOnly(p => p.Level == LogEventLevel.Information).WriteTo.File(LogFilePath("Information"), rollingInterval: RollingInterval.Day, outputTemplate: SerilogOutputTemplate))
                //            .WriteTo.Logger(lg => lg.Filter.ByIncludingOnly(p => p.Level == LogEventLevel.Warning).WriteTo.File(LogFilePath("Warning"), rollingInterval: RollingInterval.Day, outputTemplate: SerilogOutputTemplate))
                //            .WriteTo.Logger(lg => lg.Filter.ByIncludingOnly(p => p.Level == LogEventLevel.Error).WriteTo.File(LogFilePath("Error"), rollingInterval: RollingInterval.Day, outputTemplate: SerilogOutputTemplate))
                //            .WriteTo.Logger(lg => lg.Filter.ByIncludingOnly(p => p.Level == LogEventLevel.Fatal).WriteTo.File(LogFilePath("Fatal"), rollingInterval: RollingInterval.Day, outputTemplate: SerilogOutputTemplate))
                //            .CreateLogger();
                //异步写法
                Log.Logger = new LoggerConfiguration()
                            .Enrich.FromLogContext()
                            .MinimumLevel.Debug() // 所有Sink的最小记录级别
                            .WriteTo.Logger(lg => lg.Filter.ByIncludingOnly(p => p.Level == LogEventLevel.Debug).WriteTo.Async(a => a.File(LogFilePath("Debug"), rollingInterval: RollingInterval.Day, outputTemplate: SerilogOutputTemplate)))
                            .WriteTo.Logger(lg => lg.Filter.ByIncludingOnly(p => p.Level == LogEventLevel.Information).WriteTo.Async(a => a.File(LogFilePath("Information"), rollingInterval: RollingInterval.Day, outputTemplate: SerilogOutputTemplate)))
                            .WriteTo.Logger(lg => lg.Filter.ByIncludingOnly(p => p.Level == LogEventLevel.Warning).WriteTo.Async(a => a.File(LogFilePath("Warning"), rollingInterval: RollingInterval.Day, outputTemplate: SerilogOutputTemplate)))
                            .WriteTo.Logger(lg => lg.Filter.ByIncludingOnly(p => p.Level == LogEventLevel.Error).WriteTo.Async(a => a.File(LogFilePath("Error"), rollingInterval: RollingInterval.Day, outputTemplate: SerilogOutputTemplate)))
                            .WriteTo.Logger(lg => lg.Filter.ByIncludingOnly(p => p.Level == LogEventLevel.Fatal).WriteTo.Async(a => a.File(LogFilePath("Fatal"), rollingInterval: RollingInterval.Day, outputTemplate: SerilogOutputTemplate)))
                            .CreateLogger();

                Log.Debug("111111111");
                Log.Information("22222222222");
                Log.Warning("33333333333");
                Log.Error("44444444444");
                Log.Fatal("5555555555555");
                try
                {
                    int.Parse("sfsdfsf");
                }
                catch (Exception ex)
                {
                    Log.Error(ex, ex.Message);
                }
                Log.CloseAndFlush();
            }
View Code

6. 自定义输出到指定文件夹(推荐)

 思路:这里采用过滤器的来实现,需要在日志内容中定义一个属性,比如下面的{position},然后通过ByIncludingOnly(Matching.WithProperty<string>("position", p => p == "WebLog"))来筛选,然后再指定存放到那个文件夹下。

注:如果加上$符号,还要定义过滤器属性,则需要用{{}}

代码分享:

{
                static string LogFilePath(string FileName) => $@"ALL_Logs{FileName}log.log";
                string SerilogOutputTemplate = "{NewLine}Date:{Timestamp:yyyy-MM-dd HH:mm:ss.fff}{NewLine}LogLevel:{Level}{NewLine}Message:{Message}{NewLine}{Exception}" + new string('-', 100);
                Log.Logger = new LoggerConfiguration()
                            .Enrich.FromLogContext()
                            .MinimumLevel.Debug() // 所有Sink的最小记录级别
                            .WriteTo.Logger(lg => lg.Filter.ByIncludingOnly(Matching.WithProperty<string>("position", p => p == "WebLog")).WriteTo.Async(a => a.File(LogFilePath("WebLog"), rollingInterval: RollingInterval.Day, outputTemplate: SerilogOutputTemplate)))
                            .WriteTo.Logger(lg => lg.Filter.ByIncludingOnly(Matching.WithProperty<string>("position", p => p == "ApiLog")).WriteTo.Async(a => a.File(LogFilePath("ApiLog"), rollingInterval: RollingInterval.Day, outputTemplate: SerilogOutputTemplate)))
                            .WriteTo.Logger(lg => lg.Filter.ByIncludingOnly(Matching.WithProperty<string>("position", p => p == "ErrorLog")).WriteTo.Async(a => a.File(LogFilePath("ErrorLog"), rollingInterval: RollingInterval.Day, outputTemplate: SerilogOutputTemplate)))
                            .CreateLogger();
                Log.Information("{position}:111111", "WebLog");
                Log.Information("{position}:222222", "ApiLog");
                Log.Information($"{{position}}:333333", "ErrorLog");
                try
                {
                    int.Parse("sfsdfsf");
                }
                catch (Exception ex)
                {
                    Log.Error(ex, "{position}:" + ex.Message, "ErrorLog");

                }
                Log.CloseAndFlush();
}

7. 输入SQLServer(未完成)

 可参考:https://www.cnblogs.com/RainFate/p/12073662.html

三. 与Core MVC集成

前提:

 通过Nuget安装程序集:【Serilog.AspNetCore 3.4.0】,该程序集涵盖了多个子程序,可以自己封装,你可以和控制台那样自行选择需要的程序集进行使用。

1. 写法1

 类似控制台的形式,直接调用即可,可以自行封装一下, 然后在ConfigureService中实例化, 使用的时候直接通过log对象调用即可。这里封装LogUtils帮助类,可以分文件夹存储,包括普通信息的记录和Error错误,其它可自行封装。

PS:这种写法和上述控制台的类似,根据需要引入需要的程序集即可。

日志帮助类封装代码:

   /// <summary>
    /// 日志帮助类
    /// </summary>
    public class LogUtils
    {
        static string log1Name = "WebLog";
        static string log2Name = "ApiLog";
        static string log3Name = "ErrorLog";

        /// <summary>
        /// 初始化日志
        /// </summary>
        public static void InitLog()
        {
            static string LogFilePath(string FileName) => $@"{AppContext.BaseDirectory}ALL_Logs{FileName}log.log";
            string SerilogOutputTemplate = "{NewLine}Date:{Timestamp:yyyy-MM-dd HH:mm:ss.fff}{NewLine}LogLevel:{Level}{NewLine}Message:{Message}{NewLine}{Exception}" + new string('-', 100);
            Log.Logger = new LoggerConfiguration()
                        .Enrich.FromLogContext()
                        .MinimumLevel.Debug() // 所有Sink的最小记录级别
                        .WriteTo.Logger(lg => lg.Filter.ByIncludingOnly(Matching.WithProperty<string>("position", p => p == log1Name)).WriteTo.Async(a => a.File(LogFilePath(log1Name), rollingInterval: RollingInterval.Day, outputTemplate: SerilogOutputTemplate)))
                        .WriteTo.Logger(lg => lg.Filter.ByIncludingOnly(Matching.WithProperty<string>("position", p => p == log2Name)).WriteTo.Async(a => a.File(LogFilePath(log2Name), rollingInterval: RollingInterval.Day, outputTemplate: SerilogOutputTemplate)))
                        .WriteTo.Logger(lg => lg.Filter.ByIncludingOnly(Matching.WithProperty<string>("position", p => p == log3Name)).WriteTo.Async(a => a.File(LogFilePath(log3Name), rollingInterval: RollingInterval.Day, outputTemplate: SerilogOutputTemplate)))
                        .CreateLogger();
        }


        /*****************************下面是不同日志级别*********************************************/
        // FATAL(致命错误) > ERROR(一般错误) > Warning(警告) > Information(一般信息) > DEBUG(调试信息)>Verbose(详细模式,即全部)



        /// <summary>
        /// 普通日志
        /// </summary>
        /// <param name="msg"></param>
        /// <param name="fileName"></param>
        public static void Info(string msg, string fileName="")
        {
            if (fileName==""|| fileName == log1Name)
            {
                Log.Information($"{{position}}:{msg}", log1Name);
            }
            else if (fileName == log2Name)
            {
                Log.Information($"{{position}}:{msg}", log2Name);
            }
            else
            {
                //输入其他的话,还是存放到第一个文件夹
                Log.Information($"{{position}}:{msg}", log1Name);
            }
        }

        /// <summary>
        /// Error方法统一存放到ErrorLog文件夹
        /// </summary>
        /// <param name="msg"></param>
        public static void Error(Exception ex)
        {
            Log.Error(ex, "{position}:" + ex.Message, log3Name);
        }


    }
View Code

ConfigureService注册:

 public void ConfigureServices(IServiceCollection services)
 {
      //日志初始化
      LogUtils.InitLog();

      services.AddControllersWithViews();
 }

调用代码:

 public IActionResult Index()
 {
      LogUtils.Info("111111111", "WebLog");
      LogUtils.Info("222222222", "ApiLog");

       try
       {
             int.Parse("sfsdfafd");
        }
        catch (Exception ex)
        {
                LogUtils.Error(ex);
         }
         return View();
}

2. 写法2

(1). 在Program中通过UseSerilog进行初始化日志,和上述基本类似,特别注意,这里要重写一下系统自带的日志捕捉,可以写为MinimumLevel.Override("Microsoft", LogEventLevel.Fatal) 否则,生成的日志文件中会生成很多我们不需要的日志信息。

(2). 在控制器中注入ILogger对象,使用即可。

注:这里实际上覆盖系统自带的日志。

program代码分享: 

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();

                    //初始化日志
                    webBuilder.UseSerilog((hostingContext, loggerConfiguration) =>
                    {
                        string SerilogOutputTemplate = "{NewLine}Date:{Timestamp:yyyy-MM-dd HH:mm:ss.fff}{NewLine}LogLevel:" +
                                                       "{Level}{NewLine}Message:{Message}{NewLine}{Exception}" + new string('-', 100);
                        loggerConfiguration
                        .MinimumLevel.Debug()
                        .MinimumLevel.Override("Microsoft", LogEventLevel.Fatal)
                        .Enrich.FromLogContext()
                       .WriteTo.File($"{AppContext.BaseDirectory}MyLogs\log.log", rollingInterval: RollingInterval.Day, outputTemplate: SerilogOutputTemplate);
                    });

                });

调用代码分享:

public class Home2Controller : Controller
    {
        private readonly ILogger<Home2Controller> _logger;
        public Home2Controller(ILogger<Home2Controller> logger)
        {
            _logger = logger;
        }

        public string Index()
        {

            for (int i = 0; i < 50; i++)
            {
                Task.Run(() =>
                {
                    _logger.LogInformation($"测试日志信息:{Guid.NewGuid().ToString("N")}");

                });
            }
            return "ok";
        }
    }

!

  • 作       者 : Yaopengfei(姚鹏飞)
  • 博客地址 : http://www.cnblogs.com/yaopengfei/
  • 声     明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
  • 声     明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
 
原文地址:https://www.cnblogs.com/yaopengfei/p/14261414.html