Search code examples
c#asp.net-core-webapijaegeropentracingdistributed-tracing

ASP.NET Core Web API and Jaeger - Can I change the top level Span name?


I am doing some prototyping of Jaeger Tracing for an ASP.NET Core Web API and I am able to get it working using the All in One instance of Jaeger described in the Getting Started documentation and the following code in my Startup.ConfigureServices() method:

services.AddOpenTracing();

services.AddSingleton<ITracer>(serviceProvider =>
{
    string serviceName = serviceProvider.GetRequiredService<IWebHostEnvironment>().ApplicationName;

    ILoggerFactory loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();

    // Using the UDP sender
    Jaeger.Senders.Thrift.UdpSender udpSender = new Jaeger.Senders.Thrift.UdpSender();

    var reporter = new RemoteReporter.Builder().WithLoggerFactory(loggerFactory).WithSender(udpSender).Build();
    var sampler = new ConstSampler(true); //new GuaranteedThroughputSampler(samplingRate, lowerBound);
    
    ITracer tracer = new Tracer.Builder(serviceName).WithLoggerFactory(loggerFactory).WithReporter(reporter).WithSampler(sampler).Build();

    // Allows code that can't use DI to also access the tracer.
    GlobalTracer.Register(tracer);

    return tracer;
});

services.Configure<HttpHandlerDiagnosticOptions>(options =>
{
    //options.IgnorePatterns.Add(x => false);
    options.IgnorePatterns.Add(x => x.RequestUri.IsLoopback);
    
    options.OnRequest = (span, request) => 
    {
        span.SetTag("MyCustomTag", request.RequestUri.AbsoluteUri);
    };

    options.OperationNameResolver = (request) => 
    {
        return "X-" + request.RequestUri.AbsoluteUri;
    };
});

To use all this, you need to add a few packages to your project:

  1. dotnet add package OpenTracing.Contrib.NetCore --version 0.6.2
  2. dotnet add package Jaeger --version 0.4.1

So this works OK and I get Traces with their Spans showing up in my Jaeger Search UI (http://localhost:16686/search) but it just shows the Trace with my service name (in this case "MySuperCoolWebAPI") followed by "HTTP GET":

jaeger ui screenshot

This is not terribly useful to see "HTTP GET" there. Instead, I want to see the Web API action name or something else that lets me know what kind of request this really is.

As you can see from my sample code above, I have tried setting the HttpHandlerDiagnosticOptions.OperationNameResolver but this only affects HttpClient calls I make from within my web service. It does not seem to affect how the Trace/Span associated with the request I received is named.

I also tried setting the Span OperationName in my Web API Controller method using the GlobalTracer like this but this affects an inner span and NOT the main Trace/Span that shows up on the main Jaeger UI search results page:

OpenTracing.Util.GlobalTracer.Instance.ActiveSpan.SetOperationName("My Web API Action Name");

Here you can see the first child Span has its name set to what I forced it to but the main level Span (the parent of the one I changed) is not affected:

jaeger ui screenshot with child span

Is there a way I can set the operation name of the main Span with the Jaeger C# Client?

Also, I am using .NET Core 3.1 in case that is relevant.


Solution

  • After digging around in the OpenTracing C# .NET Core source (https://github.com/opentracing-contrib/csharp-netcore) I figured out how to override the top level Span.OperationName.

    I had to update my Startup.ConfigureServices() call to services.AddOpenTracing() to the following:

    services.AddOpenTracingCoreServices(otBuilder =>
    {
        otBuilder.AddAspNetCore()
            .ConfigureAspNetCore(options =>
            {
                options.Hosting.OperationNameResolver = (httpContext) =>
                {
                    return $"MySuperCoolOperationName ({httpContext.Request.Path.Value})";
                };
            })
            .AddCoreFx()
            .AddEntityFrameworkCore()
            .AddLoggerProvider();
    });