ActivitySource.StartActivity() 返回 null 是因采样器主动丢弃,非 bug;需判空处理,否则触发 NullReferenceException;名称必须完全一致(含大小写、符号),跨线程需显式传递 ActivityContext,Baggage 需手动传播。

ActivitySource.StartActivity() 返回 null 不是 bug,是采样器主动丢弃——忽略判空直接操作必炸 NullReferenceException。
StartActivity() 为什么返回 null?
不是配置错、不是没启用 tracing,而是 OpenTelemetry 默认采样策略(如 ParentBased(AlwaysOff))根据父上下文或全局开关决定是否创建 span。上游没传 traceparent、服务未启用采样、或当前请求被明确排除,都会导致 StartActivity() 返回 null。
常见错误现象:
- 调了
StartActivity()后直接SetTag("x", "y")→NullReferenceException - 日志里完全没 trace 数据,以为 exporter 挂了,其实是 span 根本没创建
开发期可临时加 AddAlwaysOnSampler() 验证逻辑是否走通;上线前必须切回合理策略(如 ParentBased(TraceIdRatioBased(0.1)))。
ActivitySource.Name 必须完全一致才生效
你在代码里写 new ActivitySource("user-service"),但 TracerProviderBuilder 只调了 AddSource("userservice"),那所有 activity 全部静默丢失——不会报错、不会警告、连日志都不会有。
名称区分大小写,连字符、点号、下划线都不能差。建议统一定义常量:
public const string SourceName = "user-service"; private static readonly ActivitySource MySource = new(SourceName);
特别注意:ASP.NET Core 的 AddAspNetCoreInstrumentation() 默认注册的是 Microsoft.AspNetCore.Hosting,不是你的服务名;gRPC 客户端需额外加 AddGrpcNetClientInstrumentation(),否则 HTTP root span 和 gRPC span 之间会断链。
跨线程/Task.Run 中 Activity.Current 为空怎么办?
Activity.Current 在 async/await 下自动流转,但在 Task.Run(() => { })、ThreadPool.QueueUserWorkItem 或同步阻塞调用中会彻底丢失上下文。
错误做法:
- 手动用
AsyncLocal<activity></activity>存取——它已被Activity.Current底层封装,直接操作会破坏一致性 - 在子线程里直接调
StartActivity()而不传父 context → 生成孤立 span
正确做法:
- 主线程捕获:
var parentContext = Activity.Current?.Context - 子线程入口显式传入:
Activity.StartActivity("name", ActivityKind.Internal, parentContext)
注意:不要用 new Activity("name").Start(),它不经过采样器,也不触发 ActivityListener,等同于无效 span。
手动传播 traceparent 时漏掉 Baggage 和 TraceState
只传 traceparent 头,下游能拿到链路 ID,但业务字段(如 tenant-id)和供应商扩展字段(如 vendor-state)全丢了。
发送方必须同时写三个头:
-
traceparent:来自activity.Context.ToTraceParentString() -
tracestate:来自activity.Context.TraceStateString -
baggage:来自Baggage.Current.ToString()或activity.Baggage.ToString()
接收方不能只靠 ActivityContext.TryParse() 解析 traceparent,还得手动重建 Baggage:
if (ActivityContext.TryParse(traceparentHeader, out var context))
{
var baggage = ActivityContext.CreateBaggage(baggageHeader);
var fullContext = new ActivityContext(context.TraceId, context.SpanId, context.TraceFlags, context.TraceStateString, baggage);
using var activity = MySource.StartActivity("name", ActivityKind.Server, fullContext);
}
Baggage 是只读集合,修改必须用 WithBaggageItem("k", "v") 生成新实例——原地改等于白改。
最易被忽略的点:Activity 不适用于数据沿袭追踪。它管的是“谁调了谁”,不是“这个字符串从哪来、被谁改过”。想追踪文件内容流转、JSON 字段血缘,得另建 DataFlowContext + AsyncLocal,别硬套 Activity。


















