通过Jaeger上报.NET应用数据

通过Jaeger为应用埋点并上报链路数据至可观测链路 OpenTelemetry 版后,可观测链路 OpenTelemetry 版即可开始监控应用,您可以查看应用拓扑、调用链路、异常事务、慢事务和SQL分析等一系列监控数据。本文介绍如何使用OpenTracing/NetCore/gRPC组件进行自动埋点或通过手动埋点并上报.NET应用数据。

重要

为获得更丰富的功能、更先进的链路追踪能力,以及最佳使用体验,建议您使用OpenTelemetry协议将应用接入可观测链路 OpenTelemetry 版

我们为您提供了详细的OpenTelemetry接入指南和最佳实践,帮助您快速上手可观测链路 OpenTelemetry 版。更多信息,请参见接入应用

重要

目前Jaeger Client C#已不再维护,建议您使用OpenTelemetry .NET上报数据。具体操作,请参见通过OpenTelemetry上报.NET应用数据

前提条件

获取接入点信息

新版控制台

  1. 登录可观测链路 OpenTelemetry 版控制台,在左侧导航栏单击接入中心

  2. 开源框架区域单击Jaeger卡片。

  3. 在弹出的Jaeger面板中选择数据需要上报的地域。

    说明

    初次接入的地域将会自动进行资源初始化。

  4. 选择连接方式上报方式,然后复制接入点信息。

    • 连接方式:若您的服务部署在阿里云上,且所属地域与选择的接入地域一致,推荐使用阿里云内网方式,否则选择公网方式。

    • 上报方式:根据客户端支持的协议类型选择HTTP或gRPC协议上报数据。

    image

旧版控制台

  1. 登录可观测链路 OpenTelemetry 版控制台

  2. 在左侧导航栏单击集群配置,然后在右侧页面单击接入点信息页签。

  3. 在页面顶部选择需要接入的地域,然后在集群信息区域打开显示Token开关。

  4. 客户端采集工具区域单击Jaeger

    相关信息列中,获取接入点信息。jager中国.jpg

    说明

    如果应用部署于阿里云生产环境,则选择阿里云VPC网络接入点,否则选择公网接入点。

背景信息

Jaeger是Uber推出的一款开源分布式追踪系统,兼容OpenTracing API,已在Uber大规模使用,且已加入CNCF开源组织。其主要功能是聚合来自各个异构系统的实时监控数据。

目前OpenTracing社区已有许多组件可支持各种.NET框架,例如:

数据是如何上报的?

  • 不通过Jaeger Agent而直接上报数据的原理如下图所示。

    image
  • 通过Jaeger Agent上报数据的原理如下图所示。

    image

示例Demo

示例代码仓库:dotnet-demo

.NET 6.0埋点

通过OpenTracing组件自动埋点

Demo源码的运行版本要求:

  • Jaeger:1.0.2版本

  • .NET:6.0版本

  1. 在示例项目的dotnet-demo/net6.0/Shared/JaegerServiceCollectionExtensions.cs文件中填写上报数据端口并修改上报服务名,用于实现ITracer对象的初始化和注册逻辑。

    public static class JaegerServiceCollectionExtensions
    {
        // 参考前提条件获取Jaeger Endpoint并填入参数
        private static readonly Uri _jaegerUri = new Uri("http://tracing-analysis-dc-sz.aliyuncs.com/adapt_your_token/api/traces");
    
        public static IServiceCollection AddJaeger(this IServiceCollection services)
        {
            if (services == null)
                throw new ArgumentNullException(nameof(services));
    
            services.AddSingleton<ITracer>(serviceProvider =>
            {
                // 可以将其修改为自定义的服务名
                string serviceName = Assembly.GetEntryAssembly().GetName().Name;
    
                ILoggerFactory loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();
    
                ISampler sampler = new ConstSampler(sample: true);
    
                IReporter reporter = new RemoteReporter.Builder()
                    .WithSender(new HttpSender.Builder(_jaegerUri.ToString()).Build())
                    .Build();
    
                ITracer tracer = new Tracer.Builder(serviceName)
                    .WithLoggerFactory(loggerFactory)
                    .WithSampler(sampler)
                    .WithReporter(reporter)
                    .Build();
    
                GlobalTracer.Register(tracer);
    
                return tracer;
            });
    
            // Prevent endless loops when OpenTracing is tracking HTTP requests to Jaeger.
            services.Configure<HttpHandlerDiagnosticOptions>(options =>
            {
                options.IgnorePatterns.Add(request => _jaegerUri.IsBaseOf(request.RequestUri));
            });
    
            return services;
        }
    }
  2. 进入项目目录dotnet-demo/net6.0/CustomersApi,然后运行以下命令。

    dotnet run --framework:net6.0
  3. 启动本地服务,并访问以下地址。

    http://localhost:5001/health
  4. 可观测链路 OpenTelemetry 版控制台应用列表页面通过自定义的serviceName搜索应用,查看上报的数据。

NET Core 3.1埋点

Demo源码的运行版本要求:

  • Jaeger:1.0.2版本

  • .NET:3.1版本

通过NetCore组件自动埋点

  1. 在项目的dotnet-demo/netcoreapp3.1/Shared/JaegerServiceCollectionExtensions.cs中填写上报数据端口并修改上报服务名,用于实现ITracer对象的初始化和注册逻辑。

    public static class JaegerServiceCollectionExtensions
    {
        // 参考前提条件获取Jaeger Endpoint并填入参数
        private static readonly Uri _jaegerUri = new Uri("http://tracing-analysis-dc-sz.aliyuncs.com/adapt_your_token/api/traces");
    
        public static IServiceCollection AddJaeger(this IServiceCollection services)
        {
            if (services == null)
                throw new ArgumentNullException(nameof(services));
    
            services.AddSingleton<ITracer>(serviceProvider =>
            {
                // 可以将其修改为自定义的服务名
                string serviceName = Assembly.GetEntryAssembly().GetName().Name;
    
                ILoggerFactory loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();
    
                ISampler sampler = new ConstSampler(sample: true);
    
                IReporter reporter = new RemoteReporter.Builder()
                    .WithSender(new HttpSender.Builder(_jaegerUri.ToString()).Build())
                    .Build();
    
                ITracer tracer = new Tracer.Builder(serviceName)
                    .WithLoggerFactory(loggerFactory)
                    .WithSampler(sampler)
                    .WithReporter(reporter)
                    .Build();
    
                GlobalTracer.Register(tracer);
    
                return tracer;
            });
    
            // Prevent endless loops when OpenTracing is tracking HTTP requests to Jaeger.
            services.Configure<HttpHandlerDiagnosticOptions>(options =>
            {
                options.IgnorePatterns.Add(request => _jaegerUri.IsBaseOf(request.RequestUri));
            });
    
            return services;
        }
    }
  2. 进入项目目录dotnet-demo/netcoreapp3.1/Shared,然后运行以下命令。

    // 添加aspnetcore中间件
    dotnet add  package OpenTracing.Contrib.NetCore
  3. 进入项目目录dotnet-demo/netcoreapp3.1/CustomersApi,然后运行以下命令。

    // 添加aspnetcore中间件
    dotnet add  package OpenTracing.Contrib.NetCore
    // 运行示例程序
    dotnet run --framework:netcoreapp3.1
  4. 启动本地服务,并访问以下地址。

    http://localhost:5001/health
  5. 可观测链路 OpenTelemetry 版控制台应用列表页面通过自定义的serviceName搜索应用,查看上报的数据。

通过gRPC组件自动埋点

  1. 安装NuGet包。

    // 添加以下组件。
    // OpenTracing.Contrib.Grpc(gRPC中间件)
    // Jaeger(OpenTracing的实现组件)
    // Microsoft.Extensions.Logging.Console(日志组件)
    
    dotnet add  package OpenTracing.Contrib.grpc
    dotnet add  package Jaeger
  2. 初始化ITracer对象。

    public static Tracer InitTracer(string serviceName, ILoggerFactory loggerFactory)
            {
                Configuration.SamplerConfiguration samplerConfiguration = new Configuration.SamplerConfiguration(loggerFactory)
                    .WithType(ConstSampler.Type)
                    .WithParam(1);
                Configuration.SenderConfiguration senderConfiguration = new Configuration.SenderConfiguration(loggerFactory)
                        // 参考前提条件获取Jaeger Endpoint并填入参数
                       .WithEndpoint("http://tracing-analysis-dc-sz.aliyuncs.com/adapt_your_token/api/traces");
                Configuration.ReporterConfiguration reporterConfiguration = new Configuration.ReporterConfiguration(loggerFactory)
                    .WithSender(senderConfiguration);
    
                return (Tracer)new Configuration(serviceName, loggerFactory)
                    .WithSampler(samplerConfiguration)
                    .WithReporter(reporterConfiguration)
                    .GetTracer();
            }
  3. 在服务端埋点。构建用于埋点的ServerTracingInterceptor对象,并将ServerTracingInterceptor绑定到服务上。

    ILoggerFactory loggerFactory = new LoggerFactory().AddConsole();
    ITracer tracer = TracingHelper.InitTracer("dotnetGrpcServer", loggerFactory);
    ServerTracingInterceptor tracingInterceptor = new ServerTracingInterceptor(tracer);
    Server server = new Server
     {
        Services = { Greeter.BindService(new GreeterImpl()).Intercept(tracingInterceptor) },
         Ports = { new ServerPort("localhost", Port, ServerCredentials.Insecure) }
     };
  4. 在客户端埋点。构建用于埋点的ClientTracingInterceptor对象,并将ClientTracingInterceptor绑定到Channel上。

    ILoggerFactory loggerFactory = new LoggerFactory().AddConsole();
    ITracer tracer = TracingHelper.InitTracer("dotnetGrpcClient", loggerFactory);
    ClientTracingInterceptor tracingInterceptor = new ClientTracingInterceptor(tracer);
    Channel channel = new Channel("127.0.0.1:50051", ChannelCredentials.Insecure);
    
    var client = new Greeter.GreeterClient(channel.Intercept(tracingInterceptor));
  5. 进入项目目录dotnet-demo/netcoreapp3.1/GreeterServer,然后在终端运行以下命令开启gRPC的服务端。

    dotnet run --framework:netcoreapp3.1
  6. 进入项目目录dotnet-demo/netcoreapp3.1/GreeterClient,然后在另一个终端运行以下命令开启gRPC的客户端。

    dotnet run --framework:netcoreapp3.1

    如果终端输出Greeting: Hello you,说明服务端和客户端之间通信成功,在控制台可以看到示例应用dotnetGrpcServerdotnetGrpcClient上报的数据。

手动埋点

除了利用各种现有插件实现埋点外,还可以使用手动埋点的方法通过Jaeger将.NET应用数据上报至可观测链路 OpenTelemetry 版控制台。

  1. 安装NuGet包。

    // Jaeger(OpenTracing的实现组件)
    // Microsoft.Extensions.Logging.Console(日志组件)
    
    dotnet add  package Microsoft.Extensions.Logging.Console
    dotnet add  package Jaeger
  2. 构建ITracer对象。ITracer是OpenTracing定义的对象,我们用Jaeger来构建该对象(配置网关、采样率等)。

    public static ITracer InitTracer(string serviceName, ILoggerFactory loggerFactory)
     {
        Configuration.SamplerConfiguration samplerConfiguration = new Configuration.SamplerConfiguration(loggerFactory)
              .WithType(ConstSampler.Type)
              .WithParam(1);
        Configuration.SenderConfiguration senderConfiguration = new Configuration.SenderConfiguration(loggerFactory)
               // 在可观测链路 OpenTelemetry 版控制台获取Jaeger Endpoint。
              .WithEndpoint("http://tracing-analysis-dc-sz.aliyuncs.com/adapt_your_token/api/traces");
    
       Configuration.ReporterConfiguration reporterConfiguration = new Configuration.ReporterConfiguration(loggerFactory)
              .WithSender(senderConfiguration);
    
       return (Tracer)new Configuration(serviceName, loggerFactory)
              .WithSampler(samplerConfiguration)
              .WithReporter(reporterConfiguration)
            .GetTracer();
    }
  3. 将ITracer注册到GlobalTracer中,方便调用代码。

    GlobalTracer.Register(InitTracer("dotnetManualDemo", loggerFactory ));
  4. 记录请求数据。

    ITracer tracer = GlobalTracer.Instance;
    ISpan span = tracer.BuildSpan("parentSpan").WithTag("mytag","parentSapn").Start();
    tracer.ScopeManager.Activate(span, false);
    // ... do something
    span.Finish();
    说明

    以上代码用于记录请求的根操作,如果需要记录请求的上一步和下一步操作,则需要调用AsChildOf方法。

    示例:

    ITracer tracer = GlobalTracer.Instance;
    ISpan parentSpan = tracer.ActiveSpan;
    ISpan childSpan =tracer.BuildSpan("childSpan").AsChildOf(parentSpan).WithTag("mytag", "spanSecond").Start();
    tracer.ScopeManager.Activate(childSpan, false);
    // ... do something
    childSpan.Finish();
  5. 可选:为了快速排查问题,您可以为某个记录添加一些自定义标签,例如记录是否发生错误、请求的返回值等。

    tracer.activeSpan().setTag("http.status_code", "200");
  6. 在分布式系统中发送RPC请求时会带上Tracing数据,包括TraceId、ParentSpanId、SpanId、Sampled等。您可以在HTTP请求中使用Extract/Inject方法在HTTP Request Headers上透传数据。总体流程如下:

    流程图

    1. 在客户端调用Inject方法传入Context信息。

      Tracer.Inject(span.Context, BuiltinFormats.HttpHeaders, new HttpHeadersInjectAdapter(request.Headers));
    2. 在服务端调用Extract方法解析Context信息。

      ISpanContext extractedSpanContext = _tracer.Extract(BuiltinFormats.HttpHeaders, new RequestHeadersExtractAdapter(request.Headers));
      ISpan childSpan = _tracer.BuildSpan(operationName).AsChildOf(extractedSpanContext);
  7. 进入项目目录dotnet-demo/netcoreapp3.1/ManualDemo,然后运行以下命令,示例程序将会上报数据。

    dotnet run --framework:netcoreapp3.1

    在控制台可以查看手动埋点的示例应用dotnetManualDemo上报的数据。

常见问题

Q1:Demo程序执行成功,为什么控制台上没有上报数据?

A1:请检查senderConfiguration配置中的Endpoint是否填写正确。

Configuration.SenderConfiguration senderConfiguration = new Configuration.SenderConfiguration(loggerFactory)
           // 在可观测链路 OpenTelemetry 版控制台获取Jaeger Endpoint。
          .WithEndpoint("http://tracing-analysis-dc-sz.aliyuncs.com/adapt_your_token/api/traces");

Q2:如何设置采样率?

A2:具体详情,请参见Jaeger采样率文档