Reputation: 51
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
.
Upvotes: 5
Views: 3152
Reputation: 101
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;
}
}
Upvotes: 0
Reputation: 55
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();
Upvotes: 0