文章目录1. 前言2. SpanCustomizer轻量链路定制器2.1 核心定义2.2 常量定义2.3 方法定义2.3.1 修改链路名称2.3.2 添加标签2.3.3 链路事件记录2.4 与 Span 的核心区别2. Span顶层核心2.1 核心定义2.2 方法定义2.2.1 状态与上下文获取2.2.2 完整生命周期管控1构建阶段2启动阶段3运行阶段4终止阶段2.3 内部类/内部接口2.3.1 枚举 Span.Kind链路类型2.3.2 接口 Span.Builder Span 构建器3. OtelSpan OpenTelemetry 桥接实现3.1 核心定义3.2 核心成员变量3.3 核心常量3.4 静态转换工具方法3.5 方法定义3.5.1 构造方法3.5.2 状态与上下文3.5.3 生命周期实现重点特性3.5.4 标签、事件、名称实现3.5.5 异常埋点3.5.6 远端服务埋点3.5.7 内部辅助方法3.5.8 通用方法覆写1. 前言Micrometer Tracing提供一套统一、抽象、可插拔的链路追踪API底层同时兼容Brave、OpenTelemetry两大主流追踪实现。整个链路追踪体系中Span是最核心的工作单元承担链路埋点、生命周期管理、标签事件记录、异常上报等核心能力。本文一次性完整拆解三大核心源码组件Span顶层核心接口定义链路单元完整生命周期与能力SpanCustomizer轻量Span定制接口无实例操作当前链路OtelSpanOpenTelemetry底层桥接实现类2. SpanCustomizer轻量链路定制器2.1 核心定义SpanCustomizer当前线程活跃Span操作门面不需要手动创建、启动、结束Span只用来修改已经存在的Span信息。核心特性作用域绑定仅操作当前线程已存在的生效 Span链式调用所有方法支持链式编程代码简洁零侵入空实现内置NOOP空实例关闭追踪时无开销、无需业务判空能力精简仅支持修改、追加数据无法创建、启停、销毁Span2.2 常量定义内置全局空实现实例链路追踪关闭时自动使用避免空指针与多余逻辑判断SpanCustomizerNOOPnewSpanCustomizer(){...};2.3 方法定义2.3.1 修改链路名称更新当前作用域Span的操作名称返回自身支持链式调用。SpanCustomizername(Stringname);2.3.2 添加标签/** * 为 Span 设置标签。 * param key 标签键 * param value 标签值 * return 当前 {link SpanCustomizer} 对象支持链式调用 */SpanCustomizertag(Stringkey,Stringvalue);内置默认重载方法支持多数据类型自动转换tag(String key, long value)长整型标签tag(String key, double value)浮点型标签tag(String key, boolean value)布尔型标签代码示例// 获取当前上下文正在运行 Span 的定制器// SpanCustomizer 仅用于修改已存在的Span不能创建新SpanSpanCustomizercustomizertracer.currentSpanCustomizer();customizer// 修改链路Span名称.name(order:create)// 添加标签用于链路检索、筛选维度.tag(order.id,10001)// 添加业务标签.tag(pay.success,true)// 在当前时间点添加Span事件记录关键行为节点.event(order_saved);2.3.3 链路事件记录向当前链路追加时序事件自动携带系统时间戳用于标记链路关键节点如请求开始、数据库查询完成、回调结束等。SpanCustomizer event(String value)/** * 在 Span 上添加事件。 * param value 事件名称 * return 当前 {link SpanCustomizer} 对象支持链式调用 */SpanCustomizerevent(Stringvalue);2.4 与 Span 的核心区别SpanCustomizer被动修改当前链路无法创建、启停、结束Span适合通用组件Span完整生命周期管控可创建、启动、结束、丢弃链路单元适合业务主动埋点2. Span顶层核心2.1 核心定义Span是Micrometer Tracing分布式链路追踪的核心顶层接口继承自SpanCustomizer。代表链路中单次独立工作单元拥有完整生命周期创建、启动、埋点、异常记录、结束、上报/丢弃。该接口设计大量参考OpenZipkin Brave同时完全兼容OpenTelemetry语义是Micrometer可插拔追踪架构的核心抽象。相关核心概念Trace一条完整分布式调用链由多个父子关联的Span组成Span单次独立操作单元链路最小埋点单元Span.Kind标记Span角色区分客户端、服务端、消息生产/消费Span.BuilderSpan构建器支持启动前精细化配置NOOP空实现实例关闭追踪时使用无上报开销常量定义SpanNOOPnewSpan(){...};空操作实现关闭追踪时返回该实例。所有埋点操作不向监控后端上报数据但依然可以正常传递链路上下文可通过isNoop()判断并跳过昂贵计算。2.2 方法定义2.2.1 状态与上下文获取boolean isNoop()判断是否为空 Span 开启追踪返回false关闭返回trueTraceContext context()获取链路上下文包含traceId、spanId、父链路ID等核心信息/** * return 返回 {code true} 代表当前Span不会执行数据采集、不会上报到外部系统。 * 但该Span上下文仍需要注入到下游请求中。开发者可利用该标识规避昂贵计算逻辑。 */booleanisNoop();/** * return 获取当前Span对应的追踪上下文 {link TraceContext} */TraceContextcontext();简单示例publicvoidhandleBusiness(){Spanspantracer.nextSpan().name(handleBusiness).start();try{// 核心演示if(!span.isNoop()){// 非空Span需要采集执行较重的标签组装逻辑StringpayloadloadHeavyBizData();span.tag(request.payload,payload);span.tag(biz.type,order);}// doProcess();}catch(Exceptione){span.error(e);}finally{span.end();}}publicvoidqueryOrder(){Spanspantracer.nextSpan().name(queryOrder).start();try{// 获取追踪上下文TraceContexttraceContextspan.context();// 获取链路唯一标识、当前SpanId、父SpanIdStringtraceIdtraceContext.traceId();StringspanIdtraceContext.spanId();StringparentSpanIdtraceContext.parentSpanId();// 日志打印链路ID方便日志与链路联动排查System.out.printf(traceId%s, spanId%s%n,traceId,spanId);// 传递TraceContext给异步线程场景手动上下文传播asyncTask(traceContext);}finally{span.end();}}2.2.2 完整生命周期管控1构建阶段调用Tracer构建Span对象此时Span尚未启动大部分核心属性允许配置一旦start()之后部分底层实现不允许修改父上下文、Kind等参数。构建阶段支持调用的方法Buildername(Stringname);Builderevent(Stringvalue);Buildertag(Stringkey,Stringvalue);Buildertag(Stringkey,longvalue);Buildertag(Stringkey,doublevalue);Buildertag(Stringkey,booleanvalue);BuildertagOfStrings(...);BuildertagOfLongs(...);BuildertagOfDoubles(...);BuildertagOfBooleans(...);Buildererror(Throwablethrowable);Builderkind(Span.KindspanKind);BuilderremoteServiceName(StringremoteServiceName);BuilderremoteIpAndPort(Stringip,intport);BuilderstartTimestamp(longstartTimestamp,TimeUnitunit);BuilderaddLink(Linklink);示例代码// 1. Builder构造阶段配置参数Span还未启动Spanspantracer.nextSpan().name(order.create).kind(Kind.SERVER).tag(order.channel,app)// 启动Span.start();2启动阶段启动Span记录起始时间戳支持链式调用标记Span正式开始返回可用Span对象。/** * 构建并启动Span * return 已启动完成的Span实例 */Spanstart();3运行阶段链式调用name()/tag()/event()/error()追加信息可设置远端服务信息remoteServiceName()、remoteIpAndPort()。运行阶段支持调用的方法Spanname(Stringname);Spanevent(Stringvalue);Spanevent(Stringvalue,longtime,TimeUnittimeUnit);Spantag(Stringkey,Stringvalue);Spantag(key,long/double/boolean);SpantagOfXXX 系列Spanerror(Throwablethrowable);SpanremoteServiceName(StringremoteServiceName);SpanremoteIpAndPort(Stringip,intport);代码示例// 2. 运行阶段业务执行过程动态追加标签、事件if(!span.isNoop()){span.tag(order.id,10086);span.event(receive_order_request);}4终止阶段void end()结束Span自动记录结束时间并上报链路数据void end(long time, TimeUnit timeUnit)自定义时间戳结束Span适配异步、回调场景void abandon()丢弃当前Span结束但不上报数据完整示例// 1. Builder构造阶段配置参数Span还未启动Spanspantracer.nextSpan().name(order.create).kind(Kind.SERVER).tag(order.channel,app)// 启动Span.start();try{// 2. 运行阶段业务执行过程动态追加标签、事件if(!span.isNoop()){span.tag(order.id,10086);span.event(receive_order_request);}doBusiness();span.event(business_finish);}catch(Throwablet){// 3. 捕获异常记录异常信息span.error(t);throwt;}finally{// 4. 【强制】生命周期收尾正常上报Spanspan.end();}2.3 内部类/内部接口2.3.1 枚举 Span.Kind链路类型用于定义Span角色区分上下游调用关系对齐OpenTelemetry规范SERVER服务端接收RPC/HTTP远程请求CLIENT客户端发起远程调用PRODUCER消息生产者向消息队列推送消息CONSUMER消息消费者消费队列消息消息队列场景的生产/消费Span无直接关键路径延迟关系区别于普通客户端服务端调用。2.3.2 接口 Span.Builder Span 构建器用于Span启动前精细化配置参数解决Span启动后部分属性无法修改的问题适配上下文提取、异步链路等复杂场景。核心能力指定父链路、清空父链路、预配置名称/标签/事件/异常、指定Span类型、远端信息、自定义启动时间、跨链路关联。内置Builder.NOOP空实现关闭追踪时无开销。3. OtelSpan OpenTelemetry 桥接实现3.1 核心定义OtelSpan是Span接口的OpenTelemetry SDK 桥接实现类。属于Micrometer Tracing适配层核心组件。核心设计思想上层业务面向Micrometer统一抽象编程底层无缝委托OTel原生SDK实现能力实现追踪框架可插拔、底层SDK解耦。3.2 核心成员变量io.opentelemetry.api.trace.Span delegateOTel原生Span委托对象所有底层能力最终由该对象实现OtelTraceContext otelTraceContextOTel链路上下文包装对象封装OTel Context与SpanContext3.3 核心常量OTel规范标准属性用于存储下游远端服务名称。staticfinalAttributeKeyStringPEER_SERVICEAttributeKey.stringKey(peer.service);3.4 静态转换工具方法提供双向转换能力实现Micrometer Span与OTel原生Span无缝互通toOtel(Span span)micrometer Span转OTel原生SpanfromOtel(span)OTel原生 Span 包装为micrometer OtelSpanfromOtel(span, context)携带自定义OTel上下文包装Span3.5 方法定义3.5.1 构造方法基于OTel原生Span构建自动初始化链路上下文携带OTel Context构建适配手动上下文传递场景基于OtelTraceContext构建复用已有上下文缓存publicOtelSpan(io.opentelemetry.api.trace.Spandelegate){this.delegatedelegate;this.otelTraceContextnewOtelTraceContext(delegate.getSpanContext(),delegate);}publicOtelSpan(io.opentelemetry.api.trace.Spandelegate,Contextcontext){this.delegatedelegate;this.otelTraceContextnewOtelTraceContext(context,delegate.getSpanContext(),delegate);}publicOtelSpan(OtelTraceContexttraceContext){this.delegatetraceContext.span!null?traceContext.span:io.opentelemetry.api.trace.Span.current();this.otelTraceContexttraceContext;}3.5.2 状态与上下文boolean isNoop()底层映射OTeldelegate.isRecording()未采集则为NOOP空SpanOtelTraceContext context()返回包装后的OTel链路上下文OverridepublicbooleanisNoop(){return!this.delegate.isRecording();}OverridepublicOtelTraceContextcontext(){if(this.delegatenull){returnnull;}returnthis.otelTraceContext;}3.5.3 生命周期实现重点特性Span start()空实现。OTel Span在Builder创建时已自动启动无需重复启动。void end()结束Span若状态为UNSET自动填充StatusCode.OK成功状态。void end(time, unit)自定义时间戳结束Span自动补全成功状态。void abandon()空实现。OTel SDK无丢弃不上报语义该方法不生效。OverridepublicSpanstart(){// they are already started via the builderreturnthis;}Overridepublicvoidend(longtime,TimeUnittimeUnit){if(this.isStatusUnset()){this.delegate.setStatus(StatusCode.OK);}this.delegate.end(time,timeUnit);}3.5.4 标签、事件、名称实现所有基础能力全部委托OTel原生API名称修改updateName()事件添加addEvent()单值标签setAttribute()适配多基础数据类型核心优化集合标签不做字符串拼接直接使用OTel原生数组类型AttributeKey存储完全贴合OTel数据规范。3.5.5 异常埋点Span error(Throwable throwable)调用OTelrecordException()记录异常堆栈强制设置Span状态为StatusCode.ERROR携带异常信息。3.5.6 远端服务埋点remoteServiceName写入peer.service标准属性remoteIpAndPort写入OTel规范网络属性network.peer.address、network.peer.port3.5.7 内部辅助方法isStatusUnset()判断Span状态是否未初始化用于end时自动补全默认成功状态。3.5.8 通用方法覆写重写toString、equals、hashCode基于底层OTel委托对象做相等判断兼容包装类拆包对比。