.NET 8 Razor应用中OpenTelemetry HttpClient埋点及ActivitySource问题
.NET 8 Razor应用中OpenTelemetry静态ActivitySource失效问题
问题背景
我有一个.NET 8 Razor Web应用,需要追踪自身执行及API调用指标,已安装以下NuGet包:
- OpenTelemetry
- OpenTelemetry.Exporter.Console
- OpenTelemetry.Instrumentation.AspNetCore
- OpenTelemetry.Instrumentation.Http
Program.cs中的配置如下:
TracerProvider tracer = null; try { ... app.MapRazorPages(); // using ResourceBuilder.CreateDefault has no effect, nor does moving init to top tracer = Sdk.CreateTracerProviderBuilder() .AddAspNetCoreInstrumentation() .AddHttpClientInstrumentation() .SetResourceBuilder(ResourceBuilder.CreateEmpty()) .AddSource("test") .AddConsoleExporter() .Build(); app.Run(); } finally { tracer?.Dispose(); }
我在服务类中尝试复用静态ActivitySource(遵循单实例最佳实践),代码如下:
class MyService : IDisposable { private static readonly ActivitySource _source1= new ActivitySource("test"); async Task<HttpResponseMessage> MyMethod() { using var activity1 = _source1.StartActivity("action"); var source2 = new ActivitySource("test"); using var activity2 = source2.StartActivity("action"); ... } void Dispose() { _source1?.Dispose(); } }
出现的现象
_source1无监听器,activity1始终为null;source2能正常获取监听器,activity2是有效对象。
根因分析
问题出在服务的注册方式与静态资源的生命周期冲突:
当使用builder.Services.AddScoped<MyService>注册服务时,Scoped服务的生命周期与请求绑定:
- 页面首次渲染时,会创建MyService的实例,请求结束后触发
Dispose方法,此时静态的_source1被释放; - 后续点击按钮触发操作时,会创建新的MyService实例,但静态成员
_source1属于类级别,不会重新初始化,已经处于Disposed状态,无法再启动Activity; - 而
source2是每次调用MyMethod时新建的实例,未被提前释放,所以能正常对接OpenTelemetry的监听器。
解决方案
切换服务注册方式为Singleton:
将builder.Services.AddScoped<MyService>改为builder.Services.AddSingleton<MyService>,这样MyService只会创建一个实例,Dispose只会在应用关闭时触发,静态ActivitySource不会被提前释放,能持续正常工作。修正静态ActivitySource的Dispose逻辑:
静态ActivitySource属于类的全局资源,不应该在实例的Dispose方法中释放,否则会导致所有实例共享的资源被提前销毁。建议移除Dispose中对_source1的释放操作,改为在应用关闭时统一处理(或者依赖OpenTelemetry的自动生命周期管理)。
内容的提问来源于stack exchange,提问作者Jason
相关产品推荐
相关产品推荐

