.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

Tags: