.NET OpenTelemetry泛型主机托管服务未添加ActivitySource监听器问题
核心原因
单元测试主机生命周期未正确管理:你在单元测试中仅调用了
Build()但未启动主机(StartAsync),导致OpenTelemetry的TracerProvider还未完成初始化,托管服务的ExecuteAsync就提前执行了,此时还没有监听器附加到ActivitySource。而长期运行的工作服务会完整启动主机,等待所有服务初始化完成后才执行托管服务逻辑,因此不会出现这个问题。静态
ActivitySource初始化时机过早:TestService中的ActivitySource是静态字段,会在类首次被访问时就创建,这个时间点可能早于OpenTelemetry注册监听器的时机,导致OpenTelemetry无法捕获到这个已存在的ActivitySource(旧版本OpenTelemetry对已存在的源支持有限)。预发布版本潜在问题:你使用的
OpenTelemetry.Extensions.Hosting是1.0.0-rc9.5预发布版本,存在一些生命周期管理的bug,可能导致TracerProvider无法正确绑定到ActivitySource。
解决方案
1. 正确管理单元测试中的主机生命周期
在单元测试中,必须启动主机并等待初始化完成后再执行测试逻辑,测试结束后停止主机:
var stoppingToken = new CancellationTokenSource().Token; var host = Host.CreateDefaultBuilder() .ConfigureServices((context, services) => { services .AddSingleton<MyClass>() .AddSingleton(new ActivitySource("TestService")) // 注入ActivitySource .AddOpenTelemetryTracing(hostingBuilder => hostingBuilder .Configure((sp, builder) => { var myClass = sp.GetRequiredService<MyClass>(); builder .AddAspNetInstrumentation() .AddHttpClientInstrumentation() .AddConsoleExporter() .AddSource("TestService*"); })) .AddHostedService<TestService>(); }) .Build(); // 启动主机,确保TracerProvider完成初始化 await host.StartAsync(stoppingToken); try { // 这里可以添加测试验证逻辑,比如等待托管服务执行完成 await Task.Delay(1000, stoppingToken); // 根据实际逻辑调整等待时间 } finally { await host.StopAsync(stoppingToken); host.Dispose(); }
2. 避免静态ActivitySource,改用依赖注入
将ActivitySource注册为单例服务并注入到TestService中,确保它在TracerProvider初始化后才被使用:
internal class TestService : BackgroundService { private readonly ActivitySource _testActivitySource; // 通过构造函数注入ActivitySource public TestService(ActivitySource testActivitySource) { _testActivitySource = testActivitySource; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { await Task.Run(() => { using (var activity = _testActivitySource.StartActivity("TestService", ActivityKind.Server)) { if (activity == null) { Console.WriteLine("Activity未创建:无监听器附加"); return; } Console.WriteLine("进入Activity代码块"); activity.SetTag("foo", 1); Console.WriteLine($"Activity标签:{string.Join(", ", activity.Tags.Select(t => $"{t.Key}={t.Value}"))}"); } Console.WriteLine("退出Activity代码块"); }, stoppingToken); } }
3. 升级OpenTelemetry包到稳定版
将预发布版本的包升级到稳定版,确保版本兼容性:
<package id="OpenTelemetry" version="1.6.0" /> <package id="OpenTelemetry.Api" version="1.6.0" /> <package id="OpenTelemetry.Exporter.Console" version="1.6.0" /> <package id="OpenTelemetry.Extensions.Hosting" version="1.6.0" /> <package id="OpenTelemetry.Instrumentation.AspNet" version="1.6.0" /> <package id="OpenTelemetry.Instrumentation.Http" version="1.6.0" />
注:移除OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule,该模块已被整合到新版AspNet Instrumentation中
内容的提问来源于stack exchange,提问作者Coal Chris

