上文咱們演示了使用NLog向ElasticSearch寫日誌的基本過程(輸出的是普通文本日誌),今天咱們來看下如何向ES輸出結構化日誌、並利用Kibana中分析日誌。html
當前互聯網、物聯網、大數據日新月異,軟件越複雜,查找任何給定問題的原由就越困難(且成本更高)。git
在實踐中咱們開發了各類規避、診斷應用程序錯誤行爲的利器:
靜態類型檢查
,自動化測試
,事件探查器
,崩潰轉儲
和監視系統
。可是記錄程序執行步驟的日誌仍然是過後診斷最豐富的數據源。github
在日誌分析時,小批量普通的文本對於人類很友好,但卻很難從大量普通文本中快速定位、精準提取特定信息。json
..... [2018-04-07T13:45:56.789Z INF] https://example.com/api/warehouse,query reserve,took 100 ms [2018-04-07T13:45:56.789Z INF] api/commitOrder,OrderId:9876543210,commit order took 50 ms ...... [2018-04-07T13:45:56.789Z INF] /login,user:Kenny,from ip_address:127.0.0.1,took 100 ms ...... [2018-04-07T13:45:56.789Z INF] https://example.com/api/warehouse,OrderId:9876543210,decrease reserve took 10000 ms [2018-04-07T13:45:56.789Z INF] /api/creatNewOrder,OrderId:9876543210, create order took 100 ms .....
普通文本對人類友好,對於機器不友好。
結構化日誌提出了Message template
來解決日誌對機器不友好的問題。api
Messgae Template: 是一個與語言無關的規範,以對人類和機器友好的格式捕獲、呈現結構化的日誌事件。app
var traceid = _.TraceIdentifier; // 【鎖定庫存】 這個動做耗時較長 _logger.LogInformation("{TraceId},{endpoint},OrderId:{orderId},decrease reserve took {elasped} ms", traceid, "https://example.com/api/warehouse", 9876543210, 10000);
注意命名佔位符,它們能如格式化字符串佔位符{0}{1}同樣佔位,並且能將屬性名稱與每一個匹配位置的消息數據相關聯,以下圖以json格式提取了關鍵消息。性能
消息模板的優點在於:既能保持普通文本的格式,又具有捕獲結構化數據的能力(對機器友好)。測試
下面來完整輸出、分析提交訂單請求
的日誌:大數據
NLog4.5引入結構化日誌,支持Message Template
, 在ASP.NET Core腳手架Startup文件
--->Configure方法
添加以下代碼:ui
app.MapWhen(_ => _.Request.Path.Value == "/" , appBuilder => appBuilder.Run(_ => { var traceid = _.TraceIdentifier; // 查詢庫存 _logger.LogInformation("{traceId},{endpoint},query reserve,took{elasped} ms", traceid, "https://example.com/api/warehouse", 100); // 建立訂單 _logger.LogInformation("{traceId},{endpoint},OrderId:{orderId}, create order took {elasped} ms", traceid, "/api/creatNewOrder", 9876543210, 100); // 鎖定庫存 _logger.LogInformation("{traceId},{endpoint},OrderId:{orderId},decrease reserve took {elasped} ms", traceid, "https://example.com/api/warehouse", 9876543210, 10000); // 提交訂單 _logger.LogInformation("{traceId},{endpoint},OrderId:{orderId},commit order took {elasped} ms", traceid, "api/commitOrder", 9876543210, 50); _.Response.StatusCode = StatusCodes.Status200OK; _.Response.WriteAsync("Generate Order OK!"); return Task.CompletedTask; }));
這裏咱們關注如何向ElasticSearch輸出結構化日誌,請務必將includeAllProperties="true"
,這樣輸出到ES的纔會包含全部事件屬性。
<target name="elastic" xsi:type="BufferingWrapper" flushTimeout="5000"> <target xsi:type="ElasticSearch" includeAllProperties="true" index="logstash-20200805" uri="${configsetting:item=ConnectionStrings.ElasticUrl}" /> </target>
這個訂單請求,會產生6條日誌(這裏你也會看到日誌的顯示順序可能不能如你所願):
下面給出[鎖定庫存]日誌ES文檔, 文檔上已經出現了關鍵的消息屬性[traceId] [endpoint] [orderId] [elasped]
{ "_index": "logstash-20200805", "_type": "logevent", "_id": "emTivXMBwcdwe4RliB9f", "_version": 1, "_score": null, "_source": { "@timestamp": "2020-08-05T17:10:00.7170456+08:00", "level": "Info", "message": "2020-08-05 17:10:00.7170|INFO|EqidManager.Startup|0HM1P3TAGNJ5Q:00000001,https://example.com/api/warehouse,OrderId:9876543210,decrease reserve took 10000 ms", "TraceId": "0HM1P3TAGNJ5Q:00000001", "endpoint": "https://example.com/api/warehouse", "orderId": 9876543210, "elasped": 10000 }, "fields": { "@timestamp": [ "2020-08-05T09:10:00.717Z" ] }, "sort": [ 1596618600717 ] }
經過Kibana界面咱們能夠便捷地完成以下分析:
本文肝時較長(elasped>=10天)
includeAllProperties=true
(默認是false), 摸索了好久