一. 簡介
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(); }
日志文件內容:
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(); }
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(); }
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); } }
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 : 原創博客請在轉載時保留原文鏈接或在文章開頭加上本人博客地址,否則保留追究法律責任的權利。