通过Jaeger为应用埋点并上报链路数据至可观测链路 OpenTelemetry 版后,可观测链路 OpenTelemetry 版即可开始监控应用,您可以查看应用拓扑、调用链路、异常事务、慢事务和SQL分析等一系列监控数据。本文介绍如何使用OpenTracing/NetCore/gRPC组件进行自动埋点或通过手动埋点并上报.NET应用数据。
为获得更丰富的功能、更先进的链路追踪能力,以及最佳使用体验,建议您使用OpenTelemetry协议将应用接入可观测链路 OpenTelemetry 版。
我们为您提供了详细的OpenTelemetry接入指南和最佳实践,帮助您快速上手可观测链路 OpenTelemetry 版。更多信息,请参见接入应用。
目前Jaeger Client C#已不再维护,建议您使用OpenTelemetry .NET上报数据。具体操作,请参见通过OpenTelemetry上报.NET应用数据。
前提条件
背景信息
Jaeger是Uber推出的一款开源分布式追踪系统,兼容OpenTracing API,已在Uber大规模使用,且已加入CNCF开源组织。其主要功能是聚合来自各个异构系统的实时监控数据。
目前OpenTracing社区已有许多组件可支持各种.NET框架,例如:
示例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
在可观测链路 OpenTelemetry 版控制台的应用列表页面通过自定义的serviceName搜索应用,查看上报的数据。
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
在可观测链路 OpenTelemetry 版控制台的应用列表页面通过自定义的serviceName搜索应用,查看上报的数据。
通过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(); }
将ITracer注册到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采样率文档。