.NetCore日誌,相信你們多少都接觸過,博客園有關 ① AspNetCore依賴注入第三方日誌組件 ②第三方日誌組件Nlog,Serilog 應用方法的博文層出不窮。git
結合程序的部署結構,本文分單體和微服務聊一聊AspNetCore中追蹤日誌流的方法。github
AspNetCore程序基於Pipeline和中間件處理請求, 根據須要記錄日誌; 生產出故障時,在數量龐大的日誌記錄中追蹤某個請求完整的處理鏈顯得頗有必要(這個深有體會)。web
針對單體程序,AspNetCore貼心的爲咱們提供了HttpContext.TraceIdentifier屬性, 這個TraceId由{ConnectionId};{Request Number}組成,理論上這個id標記了位於某Http鏈接上的某次請求。json
① 爲何由 {ConnectionId}:{Request Number}組成?app
默認大部分讀者知曉Http1.1 一個鏈接上可發起多個Http請求框架
② TraceId 中ConnectionId 有Kestrel從{0-9,a-z}中生成, 具體方式可參考:https://github.com/aspnet/KestrelHttpServer/blob/a48222378b8249a26b093b5b835001c7c7b45815/src/Kestrel.Core/Internal/Infrastructure/CorrelationIdGenerator.cs分佈式
ok, 如今着重聊一下應用方式和衍生知識點ide
① 啓用NLog日誌微服務
添加 <PackageReference Include="NLog.Web.AspNetCore" Version="4.9.0" />ui
public class Program { public static void Main(string[] args) { var webHost = WebHost.CreateDefaultBuilder(args) .ConfigureAppConfiguration((hostingContext, configureDelagate) => { //configureDelegate默認會按照以下順序加載ChainedConfiguration、appsetting.*.json,Environment、CommandLine configureDelagate.AddJsonFile($"appsettings.secrets.json", optional: true, reloadOnChange: true); }) .ConfigureLogging((hostingContext, loggingBuilder) => { loggingBuilder.AddConsole().AddDebug(); }) .UseNLog() // 默認會找工做目錄下nlog.config配置文件 .UseStartup<Startup>() .Build(); webHost.Run(); } }
很明顯,Nlog要在Pipeline中自由獲取HttpContext屬性,這裏須要註冊 IHttpContextAccessor
public class Startup { // rest of the code omitted for brevity public void ConfigureServices(IServiceCollection services) { services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); } // rest of the code omitted for brevity }
② 配置Nlog 以支持 TraceId
實際上nlog支持記錄不少HttpContext信息,詳情請關注https://nlog-project.org/config/?tab=layout-renderers。
下面的Nlog配置文件呈現了TraceId & User_Id, (業務上的UserId能幫助咱們在茫茫日誌中快速縮小日誌)
<?xml version="1.0" encoding="utf-8" ?> <nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" autoReload="true" throwExceptions="false" internalLogFile="internal-nlog.txt"> <variable name="logDir" value="logs/${date:format=yyyyMMdd}" /> <!-- 日誌存儲文件夾--> <variable name="format" value="${date:format=yy/MM/dd HH\:mm\:ss} [${level}].[${logger}].[${aspnet-TraceIdentifier}].[${aspnet-user-identity}]${newline}${message} ${exception:format=tostring}" /> <targets> <target name="info" xsi:type="File" layout="${format}" fileName="${logDir}/info.log" encoding="utf-8"/> </targets> <rules> <logger name="*" minlevel="Info" writeTo="info" /> </rules> </nlog>
結果以下:
以上是在單體程序內根據traceid追蹤請求流的方法。
進一步思考,在微服務中,各服務獨立造成TraceId,在初始階段生成 TraceId 並在各微服務中保持該Traceid便可追蹤微服務的請求流。
這裏首先假設你的微服務/ 分佈式服務已經部署ELK 等日誌幾種採集處理框架,沒有部署ELK也可將多個服務的日誌寫到同一個物理文件夾。
隆重介紹輪子CorrelationId
CorrelationId是經過自定義Header來標記TraceId概念
CorrelationId 在首次收到請求時自定義名爲【X-Correlation-ID
】 的請求頭,在本服務Response寫入該Header
後置服務檢測到請求頭中包含該Header, 將該CorrelationId做爲本服務的TraceId 向後流轉
這樣在集中日誌中,能經過某TraceID追蹤微服務/分佈式 全鏈路請求處理日誌。
使用方式也至關簡單:
// Install-Package CorrelationId -Version 2.1.0 public void ConfigureServices(IServiceCollection services) { services.AddMvc(); services.AddCorrelationId(); }
通常在全部請求處理Middleware以前註冊 CorrelationId, 這樣在全部中間件就能獲取到 TraceId:
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { app.UseCorrelationId(); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseMvc(); }
打算應用該TraceId追蹤全流程請求日誌的服務都須要包含 中間件。
Ok, 本文由淺入深TraceID在單體程序和 分佈式程序中的應用, 但願對你們在日誌排障時有所幫助。