在 .NET Core 和 ASP.NET Core 中使用 Serilog

前言:

本文使用 .NET Core SDK 3.1 和 ==Serilog 3.1.2 == 的版本。
與.NET的其他日誌記錄庫不同,在 Serilog 中和日誌消息一起傳遞的參數不會破壞性地呈現爲文本格式,而是作爲機構化數據保留。

SerilogNuGet 包中,Serilog.AspNetCore 是所有常用包的集合。
所以你不管是控制檯程序還是Web程序直接引入 Serilog.AspNetCore 即可:

	PM> Install-Package Serilog.AspNetCore -Version 3.1.2

一、配置基礎

該示例在控制檯運用程序中運行

#region Main Function
    var log = new LoggerConfiguration()
        .MinimumLevel.Debug()
        .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
        .WriteTo.Console(restrictedToMinimumLevel: LogEventLevel.Information)
        .WriteTo.File(path: "../../../logs/log_.txt", rollingInterval: RollingInterval.Day)
        .CreateLogger();
    
    log.Verbose("this is Verbose.");
    
    Log.Logger = log;
    
    Log.Verbose("this is Verbose.");
    Log.Debug("this is Debug.");
    Log.Information("this is Information.");
    
	Log.CloseAndFlush(); // 初始化 Serilog 的所有配置
#endregion

我們在創建 logger 時,需要使用 LoggerConfiguration 對象。

可以使用 WriteTo 來配置日誌的接收器,這裏將其配置到了控制檯和文本文件中,這裏文件的記錄週期設置爲每天。

執行代碼我們發現控制檯中只顯示了最後一條日誌記錄,而文件中記錄了最後兩條日誌記錄,
Serilog 中有 最低級別 的概念,可以直接使用 MinimumLevel 以配置 Serilog 所有日誌接收器的接受等級。
也可以在 接收器 中單獨配置 最低級別,一律使用 restrictedToMinimumLevel 參數,這裏對控制檯接收器進行了單獨配置。

二、 格式化輸出

在== Serilog== 中有不同的輸出格式設置

1) 設置純文本的格式

在控制檯或者文本文件的接收器中,通常都能夠使用 outputTemplate 參數來控制日誌事件數據的格式設置方式。

#region
    Log.Logger = new LoggerConfiguration()
        .WriteTo.Console(outputTemplate:
        "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}")
        .CreateLogger();
#endregion

輸出模板中有許多內置屬性:

  • Timestamp - 當前日誌記錄的時間
  • Level - 日誌時間的級別,可以切換大小寫 {Level:u3} {Level:w3}
  • Message - 日誌的信息,以本文呈現,格式設置可以使嵌入的數據使用JSON或者文本格式呈現 :l :j
  • NewLine - 爲System.Environment.NewLine的屬性,跨平臺換行符
  • Exception - 完整異常信息和堆棧跟蹤,如果沒有傳入異常參數則爲空。
  • Properties - 所有未出現在輸出中其他位置的事件屬性值。 以JSON格式顯示 :j

事件的屬性(包括使用Enrich附加的屬性)也可以顯示在輸出模板中

實現擴充器
擴充器是添加、刪除或修改附加到日誌事件的屬性的簡單組件。

#region
    class DateTimeNowEnricher : ILogEventEnricher
    {
        public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
        {
            logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty(
                    "DateTimeNow", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")));
        }
    }
#endregion

#endregion
    Log.Logger = new LoggerConfiguration()
     .Enrich.With(new DateTimeNowEnricher())
     .WriteTo.Console(
         outputTemplate: "[{DateTimeNow} {Level}] {Message}{NewLine}{Exception}")
     .CreateLogger();
    
    Log.Information("Test");
#region

在接收器中我們將得到如下:
 ConsoleImg
可以看到自定義的 DateTimeNow 已經生效了。

還可以用 WithProperty 簡化配置:

    Log.Logger = new LoggerConfiguration()
        .Enrich.WithProperty("Version", "1.0.0")
        .WriteTo.Console(
         outputTemplate: "[{Timestamp:HH:mm:ss} {Level} {Version}] {Message}{NewLine}{Exception}")
     .CreateLogger();
    
    Log.Information("Test");

在接收器中我們將得到如下:
 ConsoleImg
使用 WithProperty 配置的,將是一個靜態值,例如 DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") 是行不通的。

2) 設置格式爲JSON

Serilog 中可以配置接收器記錄日誌爲JSON

    Log.Logger = new LoggerConfiguration()
        .WriteTo.Console()
        .WriteTo.File(formatter: new CompactJsonFormatter(), "../../../log.txt")
        .CreateLogger();

在以上代碼中執行以後,會發現控制檯中的日誌並沒有變成純JSON格式,是因爲需要接收器單獨配置 formatter 參數。

3) 格式提供程序

#region User
    class User
    {
        public int Id { get; set; } = 1;
        public string Name { get; set; } = "雄鹿";
        public DateTime Created { get; set; } = DateTime.Now;
    }
#endregion

#region Main Function
    Log.Logger = new LoggerConfiguration()
     // outputTemplate 默認值爲 [{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}
     .WriteTo.Console() 
     .CreateLogger();
    
    var exampleUser = new User { Id = 1, Name = "Adam", Created = DateTime.Now };
    Log.Information("Created {@User} on {Created}", exampleUser, DateTime.Now);
    Log.Information("Created {User} on {Created}", exampleUser, DateTime.Now);

    var unknown = new[] { 1, 2, 3 };
    Log.Information("Created {unknown}  on {Created}", unknown, DateTime.Now);
    Log.Information("Created {$unknown}  on {Created}", unknown, DateTime.Now);
#endregion

在接收器中我們將得到如下:
 ConsoleImg
可以看到 @ 能夠對對象進行解構,保留對象結構。用 $ 能夠強制將對象轉換爲字符串,相當於.ToString()

如果你使用的是基於文本的接收器,可以使用 outputTemplate 參數來控制日誌格式,
這裏 Timestamp 爲配置時間格式, Level 後面 u3 表示大寫,還可以用 w3 小寫。
Message 後面的 lj 表示數據將會以 JSON 格式輸出。
然後是 NewLineException,即爲如果有報錯,將會換行並輸出錯誤信息。

在字符串模板中可以使用解構運算符: @ ,解構運算符將會把對象轉換爲JSON格式,
還可以使用 $ 獲取對象的類型。

覆蓋默認格式化
在特定的情況下,可能我們需要覆蓋默認的格式化,可以通過實現 IFormatProvider 接口來定義。

#region CustomDateFormatter
    class CustomDateFormatter : IFormatProvider
    {
        readonly IFormatProvider basedOn;
        readonly string shortDatePattern;
        public CustomDateFormatter(string shortDatePattern, IFormatProvider basedOn)
        {
            this.shortDatePattern = shortDatePattern;
            this.basedOn = basedOn;
        }
        public object GetFormat(Type formatType)
        {
            if (formatType == typeof(DateTimeFormatInfo))
            {
                var basedOnFormatInfo = (DateTimeFormatInfo)basedOn.GetFormat(formatType);
                var dateFormatInfo = (DateTimeFormatInfo)basedOnFormatInfo.Clone();
                dateFormatInfo.ShortDatePattern = this.shortDatePattern;
                return dateFormatInfo;
            }
            return this.basedOn.GetFormat(formatType);
        }
    }
#endregion

#region Main Function
    var formatter = new CustomDateFormatter("yyyy-MM-dd", new CultureInfo("zh-CN"));
    Log.Logger = new LoggerConfiguration()
        .WriteTo.Console(formatProvider: formatter)
        .WriteTo.Console()
        .CreateLogger();
    
    Log.Information("Created Date {Created}", DateTime.Now);
#endregion

在接收器中我們將得到如下:
 ConsoleImg
因爲配置了兩個控制檯接收器,所以我們看到了不同的兩個輸出。

三、過濾器

可以通過篩選有選擇地記錄日誌

    Log.Logger = new LoggerConfiguration()
        .Enrich.WithProperty("Version", new Version("1.0.0.0"))
        .WriteTo.Console(
         outputTemplate: "[{Timestamp:HH:mm:ss} {Level} {Version}] {Message}{NewLine}{Exception}")
        .Filter.ByExcluding(Matching.WithProperty<string>("Version", v =>
        {
            if (new Version(v) > new Version("1.0.0.1"))
                return true; // 不記錄日誌
            else
                return false; // 記錄日誌
        }))
        .CreateLogger();
    
    Log.Information("Test");

四、在 ASP.NET Core 中引入 Serilog

此處直接提供示例,因爲內容在上方已經講解地差不多了

Serilog 在配置文件中需要單獨配置,配置如下:

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

代碼:

#region 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)
    {
        Log.Logger = new LoggerConfiguration()
            // 將配置傳給 Serilog 的提供程序 
            .ReadFrom.Configuration(Configuration)
            .MinimumLevel.Debug()
            .Enrich.FromLogContext()
            .WriteTo.Console(new RenderedCompactJsonFormatter())
            .WriteTo.File(formatter: new CompactJsonFormatter(), "logs/log_.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>();
            })
            // dispose 參數設置爲 true 會在程序退出時釋放日誌對象
            .UseSerilog(dispose: true);
#endregion

在記錄日誌時,依然是使用注入 ILogger 來記錄日誌。


參考文檔

Serilog Wiki

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章