通過Jaeger為應用埋點并上報鏈路數據至可觀測鏈路 OpenTelemetry 版后,可觀測鏈路 OpenTelemetry 版即可開始監控應用,您可以查看應用拓撲、調用鏈路、異常事務、慢事務和SQL分析等一系列監控數據。本文介紹如何使用OpenTracing/NetCore/gRPC組件進行自動埋點或通過手動埋點并上報.NET應用數據。
為獲得更豐富的功能、更先進的鏈路追蹤能力,以及最佳使用體驗,建議您使用OpenTelemetry協議將應用接入可觀測鏈路 OpenTelemetry 版。
我們為您提供了詳細的OpenTelemetry接入指南和最佳實踐,幫助您快速上手可觀測鏈路 OpenTelemetry 版。更多信息,請參見接入應用。
目前Jaeger Client C#已不再維護,建議您使用OpenTelemetry .NET上報數據。具體操作,請參見通過OpenTelemetry上報.NET應用數據。
前提條件
新版控制臺
登錄可觀測鏈路 OpenTelemetry 版控制臺,在左側導航欄單擊接入中心。
在開源框架區域單擊Jaeger卡片。
在彈出的Jaeger面板中選擇數據需要上報的地域。
說明初次接入的地域將會自動進行資源初始化。
選擇連接方式和上報方式,然后復制接入點信息。
連接方式:若您的服務部署在阿里云上,且所屬地域與選擇的接入地域一致,推薦使用阿里云內網方式,否則選擇公網方式。
上報方式:根據客戶端支持的協議類型選擇HTTP或gRPC協議上報數據。
舊版控制臺
在左側導航欄單擊集群配置,然后在右側頁面單擊接入點信息頁簽。
在頁面頂部選擇需要接入的地域,然后在集群信息區域打開顯示Token開關。
在客戶端采集工具區域單擊Jaeger。
在相關信息列中,獲取接入點信息。
說明如果應用部署于阿里云生產環境,則選擇阿里云VPC網絡接入點,否則選擇公網接入點。
背景信息
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
在可觀測鏈路 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采樣率文檔。