Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
01、提供的接收器
Serilog 使用接收器將日誌事件以各種格式寫入存儲。許多接收器由更廣泛的 Serilog 社區開發和支持;可以通過在 NuGet 上搜索 serilog 標簽找到。
02、增強器
日誌事件可以通過多種方式增強屬性。通過 NuGet 提供了一些預構建的增強器:
Install-Package Serilog.Enrichers.Thread
增強配置是通過 Enrich 配置對象進行的:
var log = new LoggerConfiguration()
.Enrich.WithThreadId()
.WriteTo.Console()
.CreateLogger();
通過日誌寫入的所有事件將攜帶一個名為 ThreadId 的屬性,表示寫入它們的托管線程的 ID。(根據約定,Enrich 上的任何 .WithXyz() 方法都會創建名為 Xyz 的屬性。)
1、日誌上下文
Serilog.Context.LogContext 可以用來動態添加和移除來自環境“執行上下文”的屬性;例如,在一個事務期間寫入的所有消息可能會攜帶該事務的 ID,等等。
此功能必須在配置時通過 .FromLogContext() 添加到日誌記錄器中:
var log = new LoggerConfiguration()
.Enrich.FromLogContext()
然後,可以使用 LogContext.PushProperty() 向上下文添加和移除屬性:
log.Information("No contextual properties");
using (LogContext.PushProperty("A", 1))
{
log.Information("Carries property A = 1");
using (LogContext.PushProperty("A", 2))
using (LogContext.PushProperty("B", 1))
{
log.Information("Carries A = 2 and B = 1");
}
log.Information("Carries property A = 1, again");
}
將屬性推送到上下文中會覆蓋任何具有相同名稱的現有屬性,直到從 PushProperty() 返回的對象被釋放,如示例中的屬性 A 所示。
重要提示:必須按照添加的確切順序從上下文中彈出屬性。否則,行為是未定義的。
2、可用的增強器包
Serilog 項目提供:
- Serilog.Enrichers.Environment - WithMachineName() 和
WithEnvironmentUserName() - Serilog.Enrichers.Process - WithProcessId()
- Serilog.Enrichers.Thread - WithThreadId()
其他有趣的增強器:
- Serilog.Web.Classic - WithHttpRequestId() 和許多其他在經典 ASP.NET 應用程式中有用的增強器
- Serilog.Exceptions - WithExceptionDetails() 添加來自異常的額外結構化屬性
- Serilog.Enrichers.Demystify - WithDemystifiedStackTraces()
- Serilog.Enrichers.ClientInfo - WithClientIp()、WithCorrelationId() 和 WithRequestHeader("header-name") 將添加具有客戶端 IP、關聯 ID 和 HTTP 請求頭值的屬性
- Serilog.Enrichers.ExcelDna - WithXllPath() 和許多其他在 Excel-DNA 插件中有用的增強器
- Serilog.Enrichers.Sensitive - WithSensitiveDataMasking() 在日誌事件中掩蓋敏感數據
- Serilog.Enrichers.GlobalLogContext - FromGlobalLogContext() 動態添加來自“全局上下文”的屬性
03、格式化輸出
Serilog 提供了多種輸出格式機制。
1、格式化純文本
寫入純文本輸出的接收器,例如控制台和基於文件的接收器,通常接受輸出模板以控制日誌事件數據的格式。
這些接收器寫入的事件格式可以使用 outputTemplate 配置參數進行修改。例如,要控制控制台接收器:
Log.Logger = new LoggerConfiguration()
.WriteTo.Console(outputTemplate:
"[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}")
.CreateLogger();
輸出模板中可以出現多個內置屬性:
- Exception - 完整的異常消息和堆棧跟蹤,以多行格式顯示。如果事件沒有關聯的異常,則為空。
- Level - 日誌事件級別,以完整級別名稱格式化。要使用更緊湊的級別名稱,可以使用格式如 {Level:u3} 或 {Level:w3} 來分別表示三個字元的大寫或小寫級別名稱。
- Message - 日誌事件的消息,呈現為純文本。:l 格式說明符可以關閉字元串的引用,:j 則使用 JSON 風格渲染任何嵌入的結構化數據。
- NewLine - 屬性值為 System.Environment.NewLine。
- Properties - 所有在輸出中未出現的事件屬性值。使用 :j 格式可以進行 JSON 渲染。
- Timestamp - 事件的時間戳,類型為 DateTimeOffset。
- TraceId - 創建事件時活動的追蹤 ID(如果有)。
- SpanId - 創建事件時活動的跨度 ID(如果有)。
通過增強器附加的事件屬性也可以出現在輸出模板中。
2、格式化 JSON
許多接收器會將日誌事件記錄為 JSON,或者可以配置為這樣做。要輸出 JSON 而不是純文本,可以指定格式化程式。以下示例使用來自 Serilog.Formatting.Compact 的格式化程式配置文件接收器。
Log.Logger = new LoggerConfiguration()
.WriteTo.File(new CompactJsonFormatter(), "log.txt")
.CreateLogger();
Serilog 項目提供了三種 JSON 格式化程式:
- Serilog.Formatting.Json.JsonFormatter - 這是 Serilog 包中歷史預設的格式化程式。它生成完整的日誌事件渲染,並支持一些配置選項。
- Serilog.Formatting.Compact.CompactJsonFormatter - 這是一個較新、更節省空間的 JSON 格式化程式,隨 Serilog.Formatting.Compact 一起提供。
- Serilog.Formatting.Compact.RenderedCompactJsonFormatter - 也是隨 Serilog.Formatting.Compact 提供的,該格式化程式將消息模板預先渲染為文本。
3、靈活的的格式化與 ExpressionTemplate
Serilog.Expressions 包含了 ExpressionTemplate 類,用於更複雜的文本和 JSON 格式化。表達式模板可以包含條件塊、重覆部分、對事件屬性的計算以及自定義格式化函數。
ExpressionTemplate 實現了 ITextFormatter 介面,因此它可以與任何基於文本的 Serilog 接收器一起使用,包括控制台(帶 ANSI 顏色主題)、文件、調試和電子郵件。
4、義格式化程式
純文本和 JSON 格式化都是通過 ITextFormatter 介面實現的。該介面的實現可以將日誌事件格式化為任何基於文本的格式。
自定義 JSON 格式化程式可以圍繞 Serilog 中包含的 JsonValueFormatter 類構建。
格式提供程式
有多種選項可用於格式化單個類型的輸出,例如日期。一個例子是大多數接收器接受的格式提供程式。
下麵是一個使用 Serilog.Sinks.Console 接收器的簡單控制台示例。這使用了預設的日期渲染行為。
class User
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime Created { get; set; }
}
public class Program
{
public static void Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.WriteTo.Console()
.CreateLogger();
var exampleUser = new User { Id = 1, Name = "Adam", Created = DateTime.Now };
Log.Information("Created {@User} on {Created}", exampleUser, DateTime.Now);
Log.CloseAndFlush();
}
}
這將以下內容寫入控制台。
[18:46:45 INF] Created {"Id": 1, "Name": "Adam", "Created": "2018-05-17T18:46:45.9064879+10:00", "$type": "User"} on 05/17/2018 18:46:45
在某些情況下,可能希望重寫或指定 DateTime 的格式。可以通過實現 IFormatProvider 來實現這一點。這種策略適用於您傳遞給 Serilog 的任何類型。
class User
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime Created { get; set; }
}
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);
}
}
public class Program
{
public static void Main(string[] args)
{
var formatter = new CustomDateFormatter("dd-MMM-yyyy", new CultureInfo("en-AU"));
Log.Logger = new LoggerConfiguration()
.WriteTo.Console(formatProvider: new CultureInfo("en-AU")) // Console 1
.WriteTo.Console(formatProvider: formatter) // Console 2
.CreateLogger();
var exampleUser = new User { Id = 1, Name = "Adam", Created = DateTime.Now };
Log.Information("Created {@User} on {Created}", exampleUser, DateTime.Now);
Log.CloseAndFlush();
}
}
以下是上述示例的輸出,配置了兩個控制台接收器。
[13:57:12 INF] Created {"Id": 1, "Name": "Adam", "Created": "2020-09-01T13:56:59.7803740-05:00", "$type": "User"} on 1/09/2020 1:57:12 PM
[13:57:12 INF] Created {"Id": 1, "Name": "Adam", "Created": "2020-09-01T13:56:59.7803740-05:00", "$type": "User"} on 01-Sep-2020 1:57:12 PM
註:相關源碼都已經上傳至代碼庫,有興趣的可以看看。https://gitee.com/hugogoos/Planner