5

Why is OpenTelemetry .NET not adding a listener for my ActivitySource in a hosted service on generic host?

I have some configurations to pass into OpenTelemetry set-up, so I depend on a class MyClass. This means registering that class, and then configuring a callback action for TracerProviderBuilder which uses that class. However, when I later create an ActivitySource and start an Activity with ActivitySource.StartActivity(...) then it returns null because no listeners were attached to the source. By debugging and inspecting the ActivitySource, I could see the list of s_activeSources which included the OpenTelemetry.HttpRequest ActivitySource created by .AddHttpClientInstrumentation() so it's odd that no listener was added for that either.

Note: This is being run in a unit test and failing, whereas it is working in a long-running worker service program. Not sure if unit tests are too short-lived or are influenced by test environment e.g. Visual Studio

Below is a minimal version of my scenario:

Host.CreateDefaultBuilder()
.ConfigureServices((context, services) =>
{
    services
    .AddSingleton<MyClass>()
    .AddOpenTelemetryTracing(hostingBuilder =>
        hostingBuilder
        .Configure(
            (sp, builder) =>
            {
                var myClass = sp.GetRequiredService<MyClass>();
                
                // Do something with myClass on builder
                
                builder
                .AddAspNetInstrumentation()
                .AddHttpClientInstrumentation()
                .AddConsoleExporter()
                .AddSource("TestService*");
            }))
    .AddHostedService<TestService>();
})
.Build();

TestService.cs

internal class TestService : BackgroundService
{
    private static ActivitySource testActivitySource = new ActivitySource("TestService");

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        await Task.Run(() =>
        {
            using (var activity = testActivitySource.StartActivity("TestService", ActivityKind.Server))
            {
                Console.WriteLine("I'm inside the activity using clause!");

                activity?.SetTag("foo", 1);

                Console.WriteLine($"activity.Tags: {activity.Tags}"); // This throws NullReferenceException since activity is null due to no listeners
            }

            Console.WriteLine("I'm outside the activity using clause!");
        });
    }
}

Package versions:

<package id="OpenTelemetry" version="1.3.0" />
<package id="OpenTelemetry.Api" version="1.3.0" />
<package id="OpenTelemetry.Exporter.Console" version="1.3.0" />
<package id="OpenTelemetry.Extensions.Hosting" version="1.0.0-rc9.5" />
<package id="OpenTelemetry.Instrumentation.AspNet" version="1.0.0-rc9.5" />
<package id="OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule" version="1.0.0-rc9.5" />
<package id="OpenTelemetry.Instrumentation.Http" version="1.0.0-rc9.5" />

I've also tried moving the .AddConsoleExporter() line out of the .Configure method and simply called straight away by hostingBuilder but still no listener attached to any ActivitySource.

1
  • Did you ever find a solution here? I'm running into the exact same thing. Commented Sep 26, 2023 at 16:55

2 Answers 2

0

In your unit test project, u have created the builder, but you haven't started the builder. Unless you don't start the Host it will not add the listener. Try using below code snippet.

var builder = Host.CreateDefaultBuilder()
            .ConfigureServices((context, services) =>
            {
                services
                .AddSingleton<MyClass>()
                .AddOpenTelemetryTracing(hostingBuilder =>
                    hostingBuilder
                    .Configure(
                        (sp, builder) =>
                        {
                            var myClass = sp.GetRequiredService<MyClass>();

                            // Do something with myClass on builder

                            builder
                            .AddAspNetInstrumentation()
                            .AddHttpClientInstrumentation()
                            .AddConsoleExporter()
                            .AddSource("TestService*");
                        }))
                .AddHostedService<TestService>();
            }).Build();

        var host = builder.Build();

        await host.StartAsync();

        await host.StopAsync();

        host.Dispose();
Sign up to request clarification or add additional context in comments.

1 Comment

I tried using both await host.StartAsync(); await host.StopAsync(); and host.RunAsync(); and both still have no listeners when debugging, causing activity to be null.
0

I use NUnit to conduct my testing of instrumentation that needs an activity created. I use the following test fixture to rig an activity source listener.

    public abstract class BaseTraceTelemetryTestFixture<TCategory>
    : BaseOptionsTestFixture<TelemetryOptions>
{
    protected readonly ActivityListener _listener;

    protected MockTracer<TCategory>? _tracer;

    protected TraceActivityAccessor? _traceActivityAccessor;

    protected virtual ITraceActivityAccessor? TraceActivityAccessor => _traceActivityAccessor;

    public BaseTraceTelemetryTestFixture()
    {
        _listener = ActivityListnerExtensions.AddActivityListener(listener =>
        {
            listener.ActivityStarted = ActivityStarted;

            listener.ActivityStopped = ActivityStopped;

            listener.ShouldListenTo = ShouldListenTo;

            listener.Sample = Sample;

            listener.SampleUsingParentId = SampleUsingParentId;
        });
    }

    /// <summary>
    /// Activity callback used to listen to the activity started event
    /// </summary>
    /// <param name="activity"></param>
    protected virtual void ActivityStarted(Activity activity)
    {

    }

    /// <summary>
    /// Activity callback used to listen to the activity stopped event
    /// </summary>
    /// <param name="activity"></param>
    protected virtual void ActivityStopped(Activity activity)
    {
        if (_tracer != null)
        {
            _tracer.ResultOfTest = activity;
        }
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="source"></param>
    /// <returns></returns>
    protected virtual bool ShouldListenTo(ActivitySource source) => true;

    protected virtual ActivitySamplingResult Sample(ref ActivityCreationOptions<ActivityContext> context)
    {
        return ActivitySamplingResult.AllData;
    }

    protected virtual ActivitySamplingResult SampleUsingParentId(ref ActivityCreationOptions<string> parentId)
    {
        return ActivitySamplingResult.AllData;
    }

    public override void SetUp()
    {
        base.SetUp();

        _traceActivityAccessor = CreateTraceActivityAccessor();

        _tracer = Substitute.ForPartsOf<MockTracer<TCategory>>(_traceActivityAccessor, _options);
    }

    public virtual TraceActivityAccessor CreateTraceActivityAccessor()
    {
        return new TraceActivityAccessor(_options);
    }

    public virtual void TestFixtureTearDown()
    {
        _listener?.Dispose();
    }

    public override void TearDown()
    {
        base.TearDown();
        _traceActivityAccessor = null;
        _tracer = null;
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.