OpenTelemetry: ActivitySource.StartActivity返回Null及traceparent标记随机问题
环境信息
- OpenTelemetry 1.4.0
- OpenTelemetry.Extensions.Hosting 1.4.0
- OpenTelemetry.Instrumentation.AspNetCore 1.0.0-rc9.14
- .NET 6.0 ASP.NET WebApi
- Docker镜像:6.0-alpine3.17
场景与问题
有两个REST API服务A和B,服务A的OpenTelemetry注册代码如下:
public static class OTRegistration { private static readonly ActivitySource _activitySource = new ActivitySource(Assembly.GetExecutingAssembly().GetName().Name!, "1.0.0"); public static void AddOT(this IServiceCollection services) { services.AddOpenTelemetry() .WithTracing(tracerProviderBuilder => tracerProviderBuilder .AddSource(_activitySource.Name) .ConfigureResource(resource => resource.AddService(_activitySource.Name)) .AddAspNetCoreInstrumentation() ); } }
客户端调用服务A后,A会调用服务B。此时A发送的traceparent头部末尾标记随机出现00或01(示例:00-000000000000000056473954588e71ac-36ae11cc57b1e9c1-00)。当标记为00时,服务B执行Activity? activity = _activitySource.StartActivity("TestActivity");会返回null,且已确认B已挂载监听者(日志显示HasListeners=True)。
根据微软文档,ActivityTraceFlags有None(0)和Recorded(1)两个值,怀疑00标记会导致Activity无法创建。
想问:服务A发送的traceparent末尾为何随机出现00和01?如何控制该行为消除随机性?
原因分析
traceparent末尾的标记对应ActivityTraceFlags.Recorded,随机性的核心原因是采样策略未明确配置:
- 你的服务A当前未指定采样器,OpenTelemetry默认使用
ParentBasedSampler。在1.4.0版本中,若未添加任何导出器(如控制台、Jaeger等),TracerProvider会动态判断是否需要采样——当认为没有记录目标时,部分Activity会被标记为Drop(对应00),部分保留为Recorded(对应01),表现出随机性。 - 服务B中
StartActivity返回null是因为OpenTelemetry的采样规则:父Activity被标记为Drop(00)时,子Activity默认会继承该状态,即使有监听者也会跳过创建。
解决方案
方法1:显式配置全采样器(强制所有链路被记录)
修改服务A的OpenTelemetry注册代码,添加SetSampler指定AlwaysOnSampler,确保所有Activity的采样状态一致为Recorded:
public static void AddOT(this IServiceCollection services) { services.AddOpenTelemetry() .WithTracing(tracerProviderBuilder => tracerProviderBuilder .AddSource(_activitySource.Name) .ConfigureResource(resource => resource.AddService(_activitySource.Name)) .AddAspNetCoreInstrumentation() // 强制开启全采样 .SetSampler(new AlwaysOnSampler()) ); }
方法2:添加导出器(明确Trace记录目标)
如果需要保留默认采样逻辑但消除随机性,添加一个导出器(如控制台导出器),让TracerProvider明确知道有记录目标,从而稳定采样状态:
public static void AddOT(this IServiceCollection services) { services.AddOpenTelemetry() .WithTracing(tracerProviderBuilder => tracerProviderBuilder .AddSource(_activitySource.Name) .ConfigureResource(resource => resource.AddService(_activitySource.Name)) .AddAspNetCoreInstrumentation() // 添加控制台导出器作为示例,也可替换为Jaeger/Zipkin等 .AddConsoleExporter() ); }
可选:服务B的局部优化
若希望服务B不受父采样状态影响,可在StartActivity时强制指定采样决策:
// 忽略父Activity的采样状态,强制创建并记录Activity Activity? activity = _activitySource.StartActivity( "TestActivity", ActivityKind.Internal, default, samplingDecision: ActivitySamplingDecision.RecordAndSample );
但更推荐从服务A统一配置采样策略,保证整条链路的Trace状态一致性。
内容的提问来源于stack exchange,提问作者Jacek Kowalski

