Appearance
AspNetCore TraceId
1. 前言
.net core 日志,相信大家多少都接触过,博客园有关:AspNetCore 依赖注入第三方日志组件、第三方日志组件 Nlog、Serilog 应用方法的博文层出不穷。结合程序的部署结构,本文分单体和微服务聊一聊 AspNetCore 中追踪日志流的方法。
2. TraceId
AspNetCore 程序基于 Pipeline 和中间件处理请求,根据需要记录日志。生产出故障时,在数量庞大的日志记录中追踪某个请求完整的处理链显得很有必要(这个深有体会)。
针对单体程序,AspNetCore 贴心的为我们提供 HttpContext.TraceIdentifier 属性,这个 TraceId 由 {ConnectionId}:{Request Number} 组成,理论上这个 id 标记了位于某 Http 连接上的某次请求。
为什么由
{ConnectionId}:{Request Number}组成?默认大部分读者知晓 Http1.1 一个连接上可发起多个 Http 请求
TraceId 中 ConnectionId 由 Kestrel 从 {0-9,a-z} 中生成,可参考 CorrelationIdGenerator.cs
ok,现在着重聊一下应用方式和衍生知识点。
2.1. 在单体应用中使用 TraceId
以 NLog 为例:
启用 NLog 日志
添加包引用
<PackageReference Include="NLog.Web.AspNetCore" Version="4.9.0" />。csharppublic 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(); } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15很明显,Nlog 要在 Pipeline 中自由获取 HttpContext 属性,这里需要注册
IHttpContextAccessorcsharppublic 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 }1
2
3
4
5
6
7配置 NLog 以支持 TraceId
实际上 NLog 支持记录很多 HttpContext 信息,详情请关注 https://nlog-project.org/config/?tab=layout-renderers
下面的 NLog 配置文件呈现了 TraceId & UserId(业务上的 UserId 能帮助我们在茫茫日志中快速缩小日志)
XML<?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>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16结果如下:

2.2. 在微服务/分布式架构中使用 TraceId
以上是在单体程序内根据 TraceId 追踪请求流的方法。
进一步思考,在微服务中,各服务独立形成 TraceId,在初始阶段生成 TraceId 并在各微服务中保持该 TraceId 即可追踪微服务的请求流。
2.2.1. CorrelationId
这里首先假设你的微服务/分布式系统已经部署了 ELK 等日志集中处理框架。(没有部署 ELK 也可将多个服务的日志写到同一个物理文件夹)
CorrelationId 是通过自定义 Header 来标记 TraceId 的
- CorrelationId 在首次收到请求时自定义名为
X-Correlation-ID的请求头,在本服务 Response 写入该 Header - 后置服务检测到请求头中包含该 Header,将该 CorrelationId 作为本服务的 TraceId 向后流转
这样在集中日志中,能通过某 TraceId 追踪微服务/分布式系统全链路请求的处理日志。
使用方式也相当简单:
csharp
// Install-Package CorrelationId -Version 2.1.0
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddCorrelationId();
}1
2
3
4
5
6
2
3
4
5
6
一般在所有 Middleware 之前注册 CorrelationId,这样在所有请求处理中间件都能获取到 TraceId:
csharp
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseCorrelationId();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseMvc();
}1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
打算应用该 TraceId 追踪全流程请求日志的服务都需要包含该中间件。
Ok,本文由浅入深 TraceId 在单体程序和分布式程序中的应用,希望对大家在日志排障时有所帮助。