阿里云 链路追踪在.NET项目中使用(jaeger trace)

        链路追踪是微服务体系当中很重要的一部分,它能够帮助开发者迅速发现问题,察觉系统性能瓶颈,也可以帮助我们在站点出现大量Exception的时候,迅速做出预警等。

阿里云链路追踪服务

  • 阿里云提供OpenTelemetry Trace数据的原生接入方式:通过OpenTelemetry接入C# Trace
  • 也可以通过SkyWalking、Jaeger、Zipkin等方式接入
  • Trace实例可以与阿里云日志服务进行绑定(通过project,logstore, 以及索引字段)。接入trace之后,日志当中会记录下uber-trace-id,需要手动开启该字段索引,才能完成绑定。

 

 通过Jaeger上报.NET应用数据(点击查看官网示例)

通常我们的项目会分为开发,测试,线上等多重环境。因此,建议大家在接入trace时,通过ServiceName的命名进行区分。

修改Span名称生成策略

以下是.NET 5.0版本的jaeger trace接入方法。这里对AspNetCore的Span名称生成策略,以及Http请求Span名称生成策略进行了调整。 另外.NET 5.0以上版本的Span过滤方式也与官网描述不同。

if (!string.IsNullOrWhiteSpace($"{Configuration["JaegerToken"]}"))
            {
                services.AddOpenTracing(builder =>
                    builder.ConfigureAspNetCore(opt =>
                    {
                        opt.Hosting.OperationNameResolver = context => $"{context.Request.Method} {context.Request.Path}";
                        opt.Hosting.IgnorePatterns.Add(ctx => ctx.Request.Path.HasValue && ctx.Request.Path.Value.Contains("health"));
                        opt.Hosting.IgnorePatterns.Add(ctx => ctx.Request.Path.HasValue && ctx.Request.Path.Value.Contains("/api/traces"));
                        opt.Hosting.IgnorePatterns.Add(ctx => ctx.Request.Path.HasValue && ctx.Request.Path.Value.Contains("/swagger"));
                    })
                    .ConfigureHttpHandler(opt=> opt.OperationNameResolver = request => $"{request.Method} {request.RequestUri.Host}{request.RequestUri.AbsolutePath}")
                ); 

                services.AddSingleton<ITracer>(serviceProvider =>
                {
                    string serviceName = serviceProvider.GetRequiredService<IWebHostEnvironment>().ApplicationName;
                 
                    ILoggerFactory loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();
                    var resolver = new SenderResolver(loggerFactory).RegisterSenderFactory<ThriftSenderFactory>();
                    Configuration.SenderConfiguration senderConfiguration = new Configuration.SenderConfiguration(loggerFactory)
                        .WithSenderResolver(resolver)
                        .WithEndpoint($"{Configuration["JaegerToken"]}");

                    var tracer = new Tracer.Builder($"{serviceName}{Configuration["environmentName"]}")
                          .WithSampler(new ConstSampler(true))
                          .WithReporter(new RemoteReporter.Builder().WithSender(senderConfiguration.GetSender()).Build())
                          .Build();

                    GlobalTracer.Register(tracer);
                    return tracer;
                });
            }

添加了全局异常处理,如何标记Span为Exception?

通常,我们会在项目中增加全局异常处理机制,而jaeger trace只会将发生UnhandledException的Span标记为异常。因此,我们需要在全局异常处理器中,主动将当前Span标记为异常,或者生成一个子Span并标记为异常。

Span可以通过设置SetTag(Tags.Error, true)方式,标记为异常。本质也就是增加一个名为“error”的tag。这样,我们就可以在阿里云统计页面,观察到包含异常Span的链路了。

public class HttpGlobalExceptionFilter : IExceptionFilter
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="context"></param>
        public void OnException(ExceptionContext context)
        {          
            var json = new AjaxResponse(new ErrorInfo(context.Exception.Message));
            context.Result = new InternalServerErrorObjectResult(json);

            if(context.Exception is not DomainException)
            {
                ITracer tracer = GlobalTracer.Instance;
                ISpan parentSpan = tracer.ActiveSpan;
                ISpan childSpan = tracer.BuildSpan($"{context.HttpContext.Request.Method} {context.HttpContext.Request.Path}").AsChildOf(parentSpan).WithTag("error", context.Exception.Message).Start();
                tracer.ScopeManager.Activate(childSpan, false);
                childSpan.Finish();
            }

            context.ExceptionHandled = true;
        }
    }

ILogger 

按照阿里云官网的介绍添加了trace之后,Nuget包:OpenTracing.Contrib.NetCore 当中其实为我们创建了一个ILogger的实现,名为OpenTracingLogger,并且在AddOpenTracing时完成注册,只不过它是访问类型为internal。

当我们再使用  _logger.LogError($"{ex.Message}");或_logger.LogInformation($"状态检测完成");

等打印时,其日志类型和内容会作为Span的Log Events保存于链路追踪当中.

OpenTracing.Contrib.NetCore

我们这个类库可以看到关于AspNetCore, HttpHandler, EFCore, SqlClient等相关Span的生成策略。

例如:在AspNetCore中,会在Microsoft.AspNetCore.Hosting.HttpRequestIn.Start,Microsoft.AspNetCore.Mvc.BeforeAction,Microsoft.AspNetCore.Mvc.BeforeActionResult三个阶段分别创建三个Span,并且依次为父子关系。

假如你认为创建一个单独的ActionResult Span并没有什么作用,并且会干扰到Trace报表的展示,那么你就可以对该项目进行二次改造和封装,设计符合自己项目的Trace方式。

猜你喜欢

转载自blog.csdn.net/qq_40404477/article/details/120265938