.NET Core整合CorrelationId實現全鏈路日誌輸出
一,鏈路追蹤
隨著微服務架構的流行,一次請求會涉及多個服務的呼叫,並且服務本身也可能會依賴其他服務,整個請求路徑會構成一個呼叫鏈,當某個節點發生異常時,整個呼叫鏈的穩定性都會受到影響,由此APM(應用效能管理)應運而生。
APM可以用於分散式追蹤、效能指標分析、應用和服務依賴分析等,可以幫助理解系統行為和分析效能問題。
常見的APM有SkyWalking,Cat、Zipkin、Pinpoint等。其中,SkyWalking可以通過SkyAPM-dotnet整合,Zipkin可以通過zipkin4net整合,對SkyWalking有興趣的童鞋可以看我另外一篇文章《.NET Core整合SkyWalking+SkyAPM-dotne實現分散式鏈路追蹤》。
雖然APM可以捕捉到整個鏈路請求,但個人感覺在日誌層面還是比較薄弱,更多的還是用於效能分析和追蹤。
在實際的生產應用中,大部分的異常更多的是業務異常,APM並不能對日誌的定位尋找產生太大幫助,最好的方案還是在日誌中輸出請求鏈路的唯一標識,利用唯一標識,我們可以很容易的在日誌中心中把請求鏈路的所有日誌檢索出來。
二,CorrelationId
CorrelationId會在請求中產生一個唯一標識,並可以將唯一標識作為一個Header傳遞到下一請求,以此類推,從而整個鏈路都可以獲取到這個標識,並自主列印到日誌當中。
下面來看一下詳細的實現:
安裝nuget包CorrelationId
修改Startup.cs
1.注入元件,可不宣告option,UpdateTraceIdentifier = true會自動更新httpcontext中TraceIdentifier欄位
1 services.AddDefaultCorrelationId(options => 2 { 3 //options.CorrelationIdGenerator = () => "Foo"; 4 //options.AddToLoggingScope = true; 5 //options.EnforceHeader = true; 6 //options.IgnoreRequestHeader = false; 7 //options.IncludeInResponse = true; 8 //options.RequestHeader = "My-Custom-Correlation-Id"; 9 //options.ResponseHeader = "X-Correlation-Id"; 10 options.UpdateTraceIdentifier = true; 11 });
2.同一鏈路中所有節點需宣告同一個Client名稱,加入此句才能將CorrelationId傳遞出去
services.AddHttpClient("MyClient") .AddCorrelationIdForwarding()
3.
app.UseCorrelationId();
到此,單個應用節點就已經配置完成了,是不是so easy。同理,其他應用節點也是這麼配置。
下面,我們來使用這個唯一標識
宣告ICorrelationContextAccessor物件,並通過建構函式注入
public class TransientClass { private readonly ICorrelationContextAccessor _correlationContext; public TransientClass(ICorrelationContextAccessor correlationContext) { _correlationContext = correlationContext; } ... }
通過_correlationContextAccessor物件即可獲取到唯一標識,後續輸出日誌時,便可列印出來
_logger.LogInformation($"[{_correlationContextAccessor.CorrelationContext.CorrelationId}]我是A");
需要特別注意的是,如呼叫的是gRPC服務,header預設是空的,無法通過配置直接帶過去,需要手動傳遞
var header = new Grpc.Core.Metadata { { "X-Correlation-Id", _correlationContextAccessor.CorrelationContext.CorrelationId } }; return await _queryClient.GetListAsync(request, header);
如設定UpdateTraceIdentifier = true,也可直接獲取HttpContext中TraceIdentifier的欄位,兩者值是一樣的
_logger.LogInformation($"[{context.GetHttpContext().TraceIdentifier}]我是B");
最後,讓我們看一下日誌輸出的效果:
附上GitHub中詳細的使用文件:https://github.com/stevejgordon/CorrelationId/wiki