You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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服务的生命周期与请求绑定:

  1. 页面首次渲染时,会创建MyService的实例,请求结束后触发Dispose方法,此时静态的_source1被释放;
  2. 后续点击按钮触发操作时,会创建新的MyService实例,但静态成员_source1属于类级别,不会重新初始化,已经处于Disposed状态,无法再启动Activity;
  3. 而source2是每次调用MyMethod时新建的实例,未被提前释放,所以能正常对接OpenTelemetry的监听器。

解决方案

  1. 切换服务注册方式为Singleton:
    将builder.Services.AddScoped<MyService>改为builder.Services.AddSingleton<MyService>,这样MyService只会创建一个实例,Dispose只会在应用关闭时触发,静态ActivitySource不会被提前释放,能持续正常工作。

  2. 修正静态ActivitySource的Dispose逻辑:
    静态ActivitySource属于类的全局资源,不应该在实例的Dispose方法中释放,否则会导致所有实例共享的资源被提前销毁。建议移除Dispose中对_source1的释放操作,改为在应用关闭时统一处理(或者依赖OpenTelemetry的自动生命周期管理)。


内容的提问来源于stack exchange,提问作者Jason

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 06:42:25