activity手动创建必须设置kind和idformat,否则跨进程传播时丢失traceid;默认构造函数创建的activity无效;需用new activity("name")后调用start()或setparentid();idformat须设为w3c以兼容tracecontext;服务端应从traceparent头解析而非新建;baggage和tracestate必须显式传播;跨线程需显式捕获并恢复activitycontext;httpclient应禁用自动追踪并手动注入traceparent。

Activity手动创建必须设置Kind和IdFormat
不设置ActivityKind或IdFormat会导致后续无法正确序列化上下文,尤其在跨进程传播时会丢失TraceId。默认构造函数创建的Activity处于Stopped状态且无有效ID,不能用于分布式追踪。
-
Activity必须用new Activity("name")构造,然后立即调用SetParentId()或Start()(后者自动分配Id) - 若需手动控制ID格式(如兼容W3C TraceContext),必须设
activity.IdFormat = ActivityIdFormat.W3C - 服务端接收请求时,应优先从HTTP头(如
traceparent)解析并用Activity.ExtractRootId()还原,而非新建
手动传播ActivityContext需同步Baggage和TraceState
仅复制Activity.TraceId和Activity.SpanId不够——Baggage(业务透传键值)和TraceState(供应商扩展字段)也需显式传递,否则下游服务无法获取完整上下文。
- 发送方:用
activity.Context.ToTraceParentString()+activity.Baggage分别写入HTTP头(traceparent、tracestate、baggage) - 接收方:用
ActivityContext.TryParse()解析traceparent,再用ActivityContext.CreateBaggage()重建Baggage - 注意
Baggage是只读集合,修改需调用WithBaggageItem()生成新实例
跨线程/异步任务中Activity不会自动继承
.NET 6+ 默认启用Activity.Current在线程/Task间自动流转,但仅限于async/await路径;手动启线程(Task.Run、ThreadPool.QueueUserWorkItem)或同步阻塞调用会丢失上下文。
- 显式捕获:在父上下文中调用
Activity.Current?.Context保存 - 显式恢复:在子线程入口处用
Activity.StartActivity(activityName, ActivityKind.Internal, context)传入原始ActivityContext - 避免使用
AsyncLocal<activity></activity>手动管理——它已被Activity.Current底层封装,直接操作易破坏一致性
HttpClient调用时不要依赖默认Activity自动创建
HttpClient在.NET 5+中默认开启Activity自动追踪,但其行为受HttpHandler配置和DiagnosticSource订阅影响,手动传播时容易与自动逻辑冲突,导致重复Span或Context覆盖。
- 禁用自动追踪:构造
HttpClient时传入new SocketsHttpHandler { ActivityHeadersPropagated = false } - 手动注入:在发送前设置
request.Headers.TryAddWithoutValidation("traceparent", context.ToTraceParentString()) - 注意
traceparent字符串必须严格符合W3C格式(如00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01),否则接收方解析失败
Baggage的显式传播和IdFormat的预设——这两个点一旦遗漏,跨语言服务(如Go/Java下游)就收不到预期的业务标识字段,排查时往往卡在“上下文看起来存在,但关键数据没了”。










