• 第三十八节: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 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
     
  • 相关阅读:
    头文件<stdarg.h>
    头文件<signal.h>
    头文件<setjmp.h>
    头文件<math.h>
    头文件<locale.h>
    头文件<limits.h>
    头文件<ctype.h>
    头文件<assert.h>
    PHP error_reporting
    八大排序算法
  • 原文地址:https://www.cnblogs.com/yaopengfei/p/14261414.html
Copyright © 2020-2023  润新知