.NET Core集成CorrelationId實現全鏈路日誌輸出
.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中詳細的使用文檔://github.com/stevejgordon/CorrelationId/wiki