在使用可观测链路 OpenTelemetry 版控制台追踪应用的链路数据之前,需要通过客户端将应用数据上报至可观测链路 OpenTelemetry 版。本文介绍如何通过Jaeger客户端上报.NET应用数据(此方法同样适用于使用C#语言开发的应用)。
前提条件
获取接入点信息
- 登录ARMS控制台,在左侧导航栏选择 。
- 在应用列表页面顶部选择目标地域,然后单击目标应用名称。
说明 语言列显示
图标的应用为接入应用监控的应用,显示-图标的应用为接入可观测链路 OpenTelemetry 版的应用。
- 在弹出的页面中,单击左侧导航栏上方的
图标,返回上一层。
- 在左侧导航栏单击集群配置,然后在右侧页面单击接入点信息页签。
- 在页面顶部选择需要接入的地域,然后在集群信息区域打开显示Token开关。
- 在客户端采集工具区域单击需要使用的链路数据采集客户端。
在下方表格的相关信息列中,获取接入点信息。
说明 如果应用部署于阿里云生产环境,则选择阿里云VPC网络接入点,否则选择公网接入点。对于Zipkin,一般情况下请使用v2版接入点,v1版接入点仅限对Zipkin十分了解的高阶用户使用。
背景信息
Jaeger是Uber推出的一款开源分布式追踪系统,兼容OpenTracing API,已在Uber大规模使用,且已加入CNCF开源组织。其主要功能是聚合来自各个异构系统的实时监控数据。
目前OpenTracing社区已有许多组件可支持各种.NET框架,例如:
数据是如何上报的?
- 不通过Jaeger Agent而直接上报数据的原理如下图所示。
- 通过Jaeger Agent上报数据的原理如下图所示。
示例Demo
示例代码仓库:dotnet-demo
.NET 6.0埋点
通过OpenTracing组件自动埋点
Demo源码的运行版本要求:
- Jaeger:1.0.2版本
- .NET:6.0版本
- 在示例项目的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; } }
- 进入项目目录dotnet-demo/net6.0/CustomersApi,然后运行以下命令。
dotnet run --framework:net6.0
- 启动本地服务,并访问以下地址。
http://localhost:5001/health
- 登录ARMS控制台后,在 页面通过自定义的serviceName搜索应用,查看上报的数据。说明 语言列显示
图标的应用为接入应用监控的应用,显示-图标的应用为接入可观测链路 OpenTelemetry 版的应用。
NET Core 3.1埋点
Demo源码的运行版本要求:
- Jaeger:1.0.2版本
- .NET:3.1版本
通过NetCore组件自动埋点
- 在项目的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; } }
- 进入项目目录dotnet-demo/netcoreapp3.1/Shared,然后运行以下命令。
// 添加aspnetcore中间件 dotnet add package OpenTracing.Contrib.NetCore
- 进入项目目录dotnet-demo/netcoreapp3.1/CustomersApi,然后运行以下命令。
// 添加aspnetcore中间件 dotnet add package OpenTracing.Contrib.NetCore // 运行示例程序 dotnet run --framework:netcoreapp3.1
- 启动本地服务,并访问以下地址。
http://localhost:5001/health
- 登录ARMS控制台后,在 页面通过自定义的serviceName搜索应用,查看上报的数据。说明 语言列显示
图标的应用为接入应用监控的应用,显示-图标的应用为接入可观测链路 OpenTelemetry 版的应用。
通过gRPC组件自动埋点
- 安装NuGet包。
// 添加以下组件。 // OpenTracing.Contrib.Grpc(gRPC中间件) // Jaeger(OpenTracing的实现组件) // Microsoft.Extensions.Logging.Console(日志组件) dotnet add package OpenTracing.Contrib.grpc dotnet add package Jaeger
- 初始化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(); }
- 在服务端埋点。构建用于埋点的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) } };
- 在客户端埋点。构建用于埋点的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));
- 进入项目目录dotnet-demo/netcoreapp3.1/GreeterServer,然后在终端运行以下命令开启gRPC的服务端。
dotnet run --framework:netcoreapp3.1
- 进入项目目录dotnet-demo/netcoreapp3.1/GreeterClient,然后在另一个终端运行以下命令开启gRPC的客户端。
dotnet run --framework:netcoreapp3.1
如果终端输出
Greeting: Hello you
,说明服务端和客户端之间通信成功,在控制台可以看到示例应用dotnetGrpcServer和dotnetGrpcClient上报的数据。
手动埋点
除了利用各种现有插件实现埋点外,还可以使用手动埋点的方法通过Jaeger将.NET应用数据上报至可观测链路 OpenTelemetry 版控制台。
- 安装NuGet包。
// Jaeger(OpenTracing的实现组件) // Microsoft.Extensions.Logging.Console(日志组件) dotnet add package Microsoft.Extensions.Logging.Console dotnet add package Jaeger
- 构建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(); }
- 将ITreacer注册到GlobalTracer中,方便调用代码。
GlobalTracer.Register(InitTracer("dotnetManualDemo", loggerFactory ));
- 记录请求数据。
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();
- 可选:为了快速排查问题,您可以为某个记录添加一些自定义标签,例如记录是否发生错误、请求的返回值等。
tracer.activeSpan().setTag("http.status_code", "200");
- 在分布式系统中发送RPC请求时会带上Tracing数据,包括TraceId、ParentSpanId、SpanId、Sampled等。您可以在HTTP请求中使用Extract/Inject方法在HTTP Request Headers上透传数据。总体流程如下:
在客户端调用Inject方法传入Context信息。
Tracer.Inject(span.Context, BuiltinFormats.HttpHeaders, new HttpHeadersInjectAdapter(request.Headers));
在服务端调用Extract方法解析Context信息。
ISpanContext extractedSpanContext = _tracer.Extract(BuiltinFormats.HttpHeaders, new RequestHeadersExtractAdapter(request.Headers)); ISpan childSpan = _tracer.BuildSpan(operationName).AsChildOf(extractedSpanContext);
- 进入项目目录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采样率文档。