前言
前兩篇分別介紹了關於 Serilog 的基礎應用與設定,這篇就來針對事件類型 Event Type 與 介紹幾個常用的 Enricher,若有問題或是錯誤的地方歡迎提出來一起討論。
Event Type
結構化日誌的好處是可以清楚的分辨"每一次"紀錄的事件,舉例來說下列簡單的代碼是透過 Serilog 寫入log 到 Console 與 File 檔案,紀錄內容是 3 筆資料
More Enrichers
在前一篇 結構化日誌 Serilog - 配置設定 中介紹了基本的應用,在 Serilog 中提供很多實用的 Enrichers 方便開發者使用,像是積木的概念如果有遇到需要的就透過 nuget 套用即可十分方便,這裡在舉例自己在專案上有用到推薦的 Enrich 項目,舉例來說如果想要在每筆 Log 都記錄 MachineName、Env、Application與 RequestID 等資訊時,目的是在 log 中紀錄下圖的資訊
在 Log 加入這些屬性的目的是為了在 ELK 搜尋時更容易分辨,如果當 Production 與 UAT 的 Log 都存放在同一台 ELK 時候,在查詢資料就可以透過 Environment 來分辨環境找到 Log 以及 Application,並自動在建立 Logger 時定義 MachineName 資訊,不用在 parser 時另外指定。
加入 Application 與 Environment
首先先從簡單的開始,分別在建立 Logger 定義 Enrich.WithProperty 屬性 Applicaion 與 Environment 並賦予相關值,在用 Json 格式輸出
加入 MachineName
接著,再加上 MachineName,需要透過 nuget 安裝 Serilog.Enrichers.Environment
並在建立 Logger 時加入 Enricher 指定 Machine
加入 RequestID
在 Serilog 中可以使用 SerilogWeb.Classic 紀錄 RequestId,使用方式很簡單透過 nuget 下載後使用 WithHttpRequestId 方法,就可以很輕鬆的達到紀錄 RequestId 的需求。在 Serilog 中有另外提供一個好用的物件 LogContext 可以讓所有的紀錄事件加入特定的屬性,使用方式是new LoggerConfiguration 加上 Enrich.FromLogContext() 擴充方法,再透過 LogContext.PushProperty 將特定的屬性以 key/value 方式加入紀錄事件中,代碼如下 Enrich.WithMachineName() 指定 Machine
另外在 LogContext 中也提供指定 source Type 方法
希望這篇的介紹對各位有幫助,Happy Coding :)
參考
Structured logging concepts in .NET Series
Serilog
前兩篇分別介紹了關於 Serilog 的基礎應用與設定,這篇就來針對事件類型 Event Type 與 介紹幾個常用的 Enricher,若有問題或是錯誤的地方歡迎提出來一起討論。
結構化日誌的好處是可以清楚的分辨"每一次"紀錄的事件,舉例來說下列簡單的代碼是透過 Serilog 寫入log 到 Console 與 File 檔案,紀錄內容是 3 筆資料
上述的代碼輸出後如下
- Log.Logger = new LoggerConfiguration()
- .WriteTo.Console()
- .WriteTo.File("logs\\log-.txt", rollingInterval: RollingInterval.Day)
- .CreateLogger();
- Log.Information("Logging Start");
- var total = 1;
- for (var i = 0; i < 3; ++i)
- {
- total *= i;
- Log.Information("Computed iteration {Counter}, total is {Total}", i, total);
- }
上述的 Log 雖然可以記錄事情但是假設資料量大或是 Log 數量很龐大的時候,找到同一事件(群)新增的 Log 紀錄是件很困難的事情,要解決這問題可以在 Log 加上一串識別字串 hashCode 方便查詢同樣事件使用。在 Serilog 解決方案是可以透過 Erinch 解決,首先新增一個類別 EventTypeEnricher 實作 ILogEventEnricher 介面的 Enrich 方法,內容為自訂 hash 的算法
- [00:35:28 INF] Logging Start
- [00:35:28 INF] Computed iteration 0, total is 0
- [00:35:28 INF] Computed iteration 1, total is 0
- [00:35:28 INF] Computed iteration 2, total is 0
接著在建立 ILogger 時候透過 Enrich.With 將產生 hash 字串內容,並自定義 outputTemplate 模板樣式
- class EventTypeEnricher : ILogEventEnricher
- {
- public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
- {
- var murmur = MurmurHash.Create32();
- var bytes = Encoding.UTF8.GetBytes(logEvent.MessageTemplate.Text);
- var hash = murmur.ComputeHash(bytes);
- var numericHash = BitConverter.ToUInt32(hash, 0);
- var eventId = propertyFactory.CreateProperty("EventType", numericHash);
- logEvent.AddPropertyIfAbsent(eventId);
- }
- }
接著重新執行一次,可以看到 log 多了自行定義的 hash Code
- Log.Logger = new LoggerConfiguration()
- .Enrich.With<EventTypeEnricher>()
- .WriteTo.Console(outputTemplate:
- "{Timestamp:HH:mm:ss} [{EventType:x8} {Level:u3}] {Message:lj}{NewLine}{Exception}")
- .CreateLogger();
成功透過自訂 Enrich 新增 hashCode 解決此問題
- 01:28:08 [4dd86bd1 INF] Logging Start
- 01:28:08 [f20ba6e0 INF] Computed iteration 0, total is 0
- 01:28:08 [f20ba6e0 INF] Computed iteration 1, total is 0
- 01:28:08 [f20ba6e0 INF] Computed iteration 2, total is 0
More Enrichers
在前一篇 結構化日誌 Serilog - 配置設定 中介紹了基本的應用,在 Serilog 中提供很多實用的 Enrichers 方便開發者使用,像是積木的概念如果有遇到需要的就透過 nuget 套用即可十分方便,這裡在舉例自己在專案上有用到推薦的 Enrich 項目,舉例來說如果想要在每筆 Log 都記錄 MachineName、Env、Application與 RequestID 等資訊時,目的是在 log 中紀錄下圖的資訊
在 Log 加入這些屬性的目的是為了在 ELK 搜尋時更容易分辨,如果當 Production 與 UAT 的 Log 都存放在同一台 ELK 時候,在查詢資料就可以透過 Environment 來分辨環境找到 Log 以及 Application,並自動在建立 Logger 時定義 MachineName 資訊,不用在 parser 時另外指定。
加入 Application 與 Environment
首先先從簡單的開始,分別在建立 Logger 定義 Enrich.WithProperty 屬性 Applicaion 與 Environment 並賦予相關值,在用 Json 格式輸出
以上代碼輸出如下,很簡單的定義了輸出包含 ApplicationName 與 Environment 的 Log 資訊
- Log.Logger = new LoggerConfiguration()
- .Enrich.WithProperty("Application", "MyApplication")
- .Enrich.WithProperty("Environment", ConfigurationManager.AppSettings["Environment"])
- .WriteTo.File(new CompactJsonFormatter(), "log.txt")
- .CreateLogger();
- {"@t":"2019-05-07T04:44:40.3369156Z","@mt":"Computed iteration {Counter}, total is {Total}","Counter":0,"Total":0,"Application":"e-Commerce","Environment":"Production"}
加入 MachineName
接著,再加上 MachineName,需要透過 nuget 安裝 Serilog.Enrichers.Environment
- Install-Package Serilog.Enrichers.Environment -Version 2.1.3
輸出的 Log 部分會加上 ServerName
- .Enrich.WithMachineName()
- {"@t":"2019-05-07T10:24:50.2274022Z","@mt":"Computed iteration {Counter}, total is {Total}","Counter":0,"Total":0,"Application":"MyApplication","Environment":"Production","MachineName":"ServerName"}
加入 RequestID
在 Serilog 中可以使用 SerilogWeb.Classic 紀錄 RequestId,使用方式很簡單透過 nuget 下載後使用 WithHttpRequestId 方法,就可以很輕鬆的達到紀錄 RequestId 的需求。在 Serilog 中有另外提供一個好用的物件 LogContext 可以讓所有的紀錄事件加入特定的屬性,使用方式是new LoggerConfiguration 加上 Enrich.FromLogContext() 擴充方法,再透過 LogContext.PushProperty 將特定的屬性以 key/value 方式加入紀錄事件中,代碼如下 Enrich.WithMachineName() 指定 Machine
在代碼中透過 pushProperty 方式將 requestId 注入到 LogContent 中,值為隨機產生GUID其結果輸出如下
- var logger = new LoggerConfiguration()
- .Enrich.FromLogContext()
- .Enrich.WithProperty("Application", "MyApplication")
- .Enrich.WithProperty("Environment", "Production")
- .WriteTo.File(new CompactJsonFormatter(), "log.txt")
- .WriteTo.Console(outputTemplate:
- "{Timestamp:HH:mm:ss} [{EventType:x8} {Level:u3}] {Message:lj}{NewLine}{Exception}")
- .CreateLogger();
- var requestId = Guid.NewGuid();
- using (LogContext.PushProperty("RequestId", requestId, true))
- {
- // Middleware Invoke
- logger.Information("This is a book, not a pencil");
- }
可以看到 Log 順利紀錄了指定的 RequestId 資訊,如果在 ASP.NET Core 中止需要將產生 RequestID 的方式加到 MiddleWare 中的 Invoke 方法,即可順利地產生出 RequestID 資訊,這裡就不在細說
- {"@t":"2019-05-07T14:56:47.2892546Z","@mt":"This is a book, not a pencil","RequestId":"0e9eaf11-544f-414d-8091-76bb7797edea","Application":"MyApplication","Environment":"Production"}
另外在 LogContext 中也提供指定 source Type 方法
想要了解更細節的部份可以看作者所撰寫的 Context and correlation – structured logging concepts in .NET,相信可以對細節與應用情境有更深入的了解。以上介紹了幾種簡單的應用,官網也有列出 enricher package 項目,如果有興趣的也可以到 github 查看使用方式及使用。
- var valueLog = Log.ForContext<valueController>();
希望這篇的介紹對各位有幫助,Happy Coding :)
參考
0 意見:
張貼留言