前言
.NetCore日志,相信大家多少都接觸過,博客園有關 ① AspNetCore依賴注入第三方日志組件 ②第三方日志組件Nlog,Serilog 應用方法的博文層出不窮。
結合程序的部署結構,本文分單體和微服務聊一聊AspNetCore中追蹤日志流的方法。
TraceID作用和組成
TraceID標記了 瀏覽器發起的某個請求, 這個id可在服務端從接收請求到 響應請求中流轉,甚至接力傳遞給下游應用中流轉,用於唯一標記和定外這次請求。
一般用於定位請求日志。
ASP.Net Core 基於中間件管道處理請求, 根據需要記錄日志; 生產出故障時,在數量龐大的日志記錄中追蹤某個請求完整的處理鏈顯得很有必要(這個深有體會)。
針對單體程序,ASP.Net Core為我們提供了HttpContext.TraceIdentifier屬性, 這個TraceId由{ConnectionId}:{Request Number}組成,理論上這個id標記了位於某Http連接上的某次請求。
① 為什么由 {ConnectionId}:{Request Number}組成?
默認大部分讀者知曉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, 現在着重聊一下使用方式和衍生知識點
ASP.NET Core使用TraceId
搭配Nlog記錄TraceId
① 啟用NLog日志
添加 <PackageReference Include="NLog.Web.AspNetCore" Version="4.9.0" />
public class Program { public static void Main(string[] args) { var webHost = WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((hostingContext, configureDelagate) => {
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 {
public void ConfigureServices(IServiceCollection services) { services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); }
}
③ 配置Nlog 顯示TraceId
TraceId在Nlog 是以Layout Renderer形式表達的:
更多的Renderer參考。下面的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即可追蹤微服務的請求流。
ASP.NET Core分布式TraceId:CorrelationId
這里首先假設你的微服務/ 分布式服務已經部署ELK 等日志幾種采集處理框架,沒有部署ELK也可將多個服務的日志寫到同一個物理文件夾。
隆重介紹輪子 CorrelationId: https:/github.com/stevejgordon/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的作用和一般組成,衍生出ASP. NETCore 單體和分布式程序中 TraceId 的使用方式, 希望對大家在日志排障時有所幫助。