After you use Jaeger to instrument a .NET application and report the trace data to Managed Service for OpenTelemetry, Managed Service for OpenTelemetry starts to monitor the .NET application. You can view the monitoring data of the application such as application topology, traces, abnormal transactions, slow transactions, and SQL analysis. This topic describes how to use the OpenTracing, .NET Core, and gRPC components to automatically or manually instrument a .NET application and report the trace data of the .NET application.
The Jaeger client for C# is no longer maintained. We recommend that you use OpenTelemetry SDK for .NET to report trace data. For more information, see Use OpenTelemetry to report the trace data of .NET applications.
Prerequisites
Background information
Jaeger is an open source distributed tracing system, which is created and used by Uber. Jaeger is compatible with the OpenTracing API and is a member of the Cloud Native Computing Foundation (CNCF). Jaeger gathers real-time monitoring data from various heterogeneous systems.
The OpenTracing community provides multiple components that support the following .NET frameworks:
Sample code
Download the sample code from dotnet-demo.
Instrument a .NET 6.0 application
Use an OpenTracing component to automatically instrument a .NET application
To run the sample code, the following requirements must be met:
Jaeger 1.0.2 is installed.
.NET 6.0 is installed.
In the dotnet-demo/net6.0/Shared/JaegerServiceCollectionExtensions.cs file of the sample project, specify the Jaeger endpoint that is used to report trace data and the name of the application of which you want to report the trace data to initialize and register the ITracer object.
public static class JaegerServiceCollectionExtensions { // Specify the Jaeger endpoint. For more information about how to obtain the Jaeger endpoint, see the "Prerequisites" section of this topic. 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 => { // Specify the application name. 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; } }
Go to the dotnet-demo/net6.0/CustomersApi directory of the sample project and run the following command:
dotnet run --framework:net6.0
Start your application and access the following URL:
http://localhost:5001/health
On the Applications page of the Managed Service for OpenTelemetry console, search for the application by application name to view the reported data.
Instrument a .NET Core 3.1 application
To run the sample code, the following requirements must be met:
Jaeger 1.0.2 is installed.
.NET Core 3.1 is installed.
Use ASP.NET Core to automatically instrument a .NET application
In the dotnet-demo/netcoreapp3.1/Shared/JaegerServiceCollectionExtensions.cs file of the sample project, specify the Jaeger endpoint that is used to report trace data and the name of the application of which you want to report the trace data to initialize and register the ITracer object.
public static class JaegerServiceCollectionExtensions { // Specify the Jaeger endpoint. For more information about how to obtain the Jaeger endpoint, see the "Prerequisites" section of this topic. 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 => { // Specify the application name. 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; } }
Go to the dotnet-demo/netcoreapp3.1/Shared directory of the sample project and run the following command:
// Add the ASP.NET Core 3.1 middleware. dotnet add package OpenTracing.Contrib.NetCore
Go to the dotnet-demo/netcoreapp3.1/CustomersApi directory of the sample project and run the following commands:
// Add the ASP.NET Core 3.1 middleware. dotnet add package OpenTracing.Contrib.NetCore // Run the demo program. dotnet run --framework:netcoreapp3.1
Start your application and access the following URL:
http://localhost:5001/health
On the Applications page of the Managed Service for OpenTelemetry console, search for the application by application name to view the reported data.
Use gRPC to automatically instrument a .NET application
Install the NuGet packages.
// Add the following components: // OpenTracing.Contrib.Grpc: the gRPC middleware. // Jaeger: an implementation component of OpenTracing. // Microsoft.Extensions.Logging.Console: a log component. dotnet add package OpenTracing.Contrib.grpc dotnet add package Jaeger
Initialize the ITracer object.
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) // Specify the Jaeger endpoint. For more information about how to obtain the Jaeger endpoint, see the "Prerequisites" section of this topic. .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(); }
Add instrumentation on the server. Build the ServerTracingInterceptor object and bind the object to the application.
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) } };
Add instrumentation on the client. Build the ServerTracingInterceptor object and bind the object to the specified 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));
Go to the dotnet-demo/netcoreapp3.1/GreeterServer directory of the sample project and run the following command on the terminal to start the gRPC server:
dotnet run --framework:netcoreapp3.1
Go to the dotnet-demo/netcoreapp3.1/GreeterClient directory of the sample project and run the following command on another terminal to start the gRPC client:
dotnet run --framework:netcoreapp3.1
If the message
Greeting: Hello you
is returned, the server and client are connected. You can view the trace data of the sample application that are reported by the dotnetGrpcServer and dotnetGrpcClient in the console.
Manually instrument a .NET application
You can instrument a .NET application by using existing plug-ins. Alternatively, you can use Jaeger to manually instrument a .NET application and report the trace data of the application to the Managed Service for OpenTelemetry console.
Install the NuGet packages.
// Jaeger: an implementation component of OpenTracing. // Microsoft.Extensions.Logging.Console: a log component. dotnet add package Microsoft.Extensions.Logging.Console dotnet add package Jaeger
Build an ITracer object. The ITracer object is an object defined by OpenTracing. You can use Jaeger to build this object. When you build this object, you can configure the object to specify the Jaeger endpoint and sample rate.
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) // Obtain the Jaeger endpoint in the Managed Service for OpenTelemetry console. .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(); }
Register the ITracer object with GlobalTracer to facilitate code calling.
GlobalTracer.Register(InitTracer("dotnetManualDemo", loggerFactory ));
Record the request data.
ITracer tracer = GlobalTracer.Instance; ISpan span = tracer.BuildSpan("parentSpan").WithTag("mytag","parentSapn").Start(); tracer.ScopeManager.Activate(span, false); // ... do something span.Finish();
NoteYou can run the preceding code to record the root operation of a request. If you want to record the previous and next operations of the request, call the AsChildOf method.
The following sample code provides an example:
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();
Optional: To troubleshoot issues in an efficient manner, you can add custom tags to a record. For example, you can add custom tags to a request to indicate whether an error occurs or describe the returned values of the request.
tracer.activeSpan().setTag("http.status_code", "200");
In a distributed system, remote procedure call (RPC) requests are sent together with trace data. Trace data contains the values of the TraceId, ParentSpanId, SpanId, and Sampled parameters. You can call the Extract or Inject method to pass through trace data in HTTP request headers. The following figure shows the entire process.
Call the Inject method on the client to specify the context information.
Tracer.Inject(span.Context, BuiltinFormats.HttpHeaders, new HttpHeadersInjectAdapter(request.Headers));
Call the Extract method on the server to extract the context information.
ISpanContext extractedSpanContext = _tracer.Extract(BuiltinFormats.HttpHeaders, new RequestHeadersExtractAdapter(request.Headers)); ISpan childSpan = _tracer.BuildSpan(operationName).AsChildOf(extractedSpanContext);
Go to the dotnet-demo/netcoreapp3.1/ManualDemo directory of the project and run the following command. The sample program starts to report trace data.
dotnet run --framework:netcoreapp3.1
In the Managed Service for OpenTelemetry console, you can view the reported trace data of the sample application dotnetManualDemo that is manually instrumented.
FAQ
Q1: What do I do if no trace data is reported to the Managed Service for OpenTelemetry console after I run the demo program?
A1: Check whether the endpoint in senderConfiguration is valid.
Configuration.SenderConfiguration senderConfiguration = new Configuration.SenderConfiguration(loggerFactory)
// Obtain the Jaeger endpoint in the Managed Service for OpenTelemetry console.
.WithEndpoint("http://tracing-analysis-dc-sz.aliyuncs.com/adapt_your_token/api/traces");
Q2: How do I specify the sample rate?
A2: For more information, visit the jaeger-client-csharp page on GitHub.