OpenTelemetry Go Jaeger Exporter 实战指南:配置、环境变量与 Span 转换原理(含弃用迁移说明)
【免费下载链接】lokiLike Prometheus, but for logs.项目地址: https://gitcode.com/GitHub_Trending/lok/loki
本指南以仓库中 vendored 的go.opentelemetry.io/otel/exporters/jaeger模块官方 README(vendor/go.opentelemetry.io/otel/exporters/jaeger/README.md)为骨架,结合该模块源码展开。你将掌握:如何用 Go 将 OpenTelemetry Span 导出到 Jaeger Agent(UDP/compact thrift)与 Jaeger Collector(HTTP/thrift),五个OTEL_EXPORTER_JAEGER_*环境变量的完整语义与优先级规则,以及 Span 在导出链路上的序列化与分包细节,同时明确该模块的弃用状态与 OTLP 迁移路径。
注意:本文面向当前仓库(Loki)中 vendor 依赖树内锁定的该 OpenTelemetry 模块版本,所有 API、默认值与行为均以仓库内实际源码为准。
模块定位与弃用状态(必读)
go.opentelemetry.io/otel/exporters/jaeger是 OpenTelemetry Go SDK 官方提供的 Jaeger Span Exporter 实现,负责把 SDK 采集到的 Span 转换为 Jaeger 数据结构并上报。但该模块目前处于已弃用(Deprecated)状态:
- OpenTelemetry 于 2023 年 7 月停止对该 Jaeger Exporter 的支持;
- Jaeger 官方已接受并推荐使用 OTLP 作为上报协议;
- 官方建议改用 OTLP Trace Exporter,即
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp或go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc。
尽管如此,该模块仍随依赖树 vendored 于当前仓库的 vendor/go.opentelemetry.io/otel/exporters/jaeger 目录下(完整实现仅约 10 个 Go 源文件:jaeger.go、env.go、agent.go、uploader.go、reconnecting_udp_client.go、doc.go等,并内嵌 Apache Thrift v0.14.1 的 vendored 副本与生成的internal/gen-go代码)。对于仍在维护存量 Jaeger 链路的读者,理解其配置方式与内部机制仍有现实意义;对新项目则建议直接走 OTLP。
安装
该模块的获取命令为:
go get -u go.opentelemetry.io/otel/exporters/jaeger由于当前仓库通过 vendor 机制固化依赖,实际使用以 vendor/go.opentelemetry.io/otel/exporters/jaeger 目录中的版本为准。该模块依赖 OpenTelemetry SDK 的sdktrace(go.opentelemetry.io/otel/sdk/trace)与语义约定包semconv/v1.21.0,并提供trace.SpanExporter接口实现,可直接挂接到sdktrace.NewTracerProvider的WithSpanProcessor上(仓库内 pkg/xcap/tracer_test.go 展示了 TracerProvider + SpanProcessor 的标准装配方式,可参考其中的sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(recorder))用法)。
快速上手示例
New是模块的入口函数,签名如下(见 jaeger.go):
func New(endpointOption EndpointOption) (*Exporter, error)它接收一个EndpointOption(通过WithAgentEndpoint或WithCollectorEndpoint构造),并会从默认resource.Default()中读取service.name作为兜底服务名——若默认资源中也取不到服务名,New会直接返回错误failed to get service name from default resource。下面是一个完整的可运行用法示例(基于模块公开 API 编写):
package main import ( "context" "log" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/exporters/jaeger" sdktrace "go.opentelemetry.io/otel/sdk/trace" ) func main() { // 方式一:上报到 Jaeger Agent(UDP,compact thrift 协议) exp, err := jaeger.New(jaeger.WithAgentEndpoint( jaeger.WithAgentHost("localhost"), jaeger.WithAgentPort("6831"), )) // 方式二:上报到 Jaeger Collector(HTTP thrift 协议) // exp, err := jaeger.New(jaeger.WithCollectorEndpoint( // jaeger.WithEndpoint("http://localhost:14268/api/traces"), // jaeger.WithUsername("admin"), // jaeger.WithPassword("secret"), // )) if err != nil { log.Fatal(err) } tp := sdktrace.NewTracerProvider( sdktrace.WithBatcher(exp), // 挂接为批量 SpanProcessor ) otel.SetTracerProvider(tp) // ... 业务代码产生 Span ... // 优雅关闭,刷新并释放导出器资源 _ = tp.Shutdown(context.Background()) }导出器实现sdktrace.SpanExporter接口(源码中以var _ sdktrace.SpanExporter = (*Exporter)(nil)静态断言),因此可与sdktrace.WithBatcher等标准处理器组合使用。Exporter的ExportSpans(jaeger.go)会先检查 context 是否已取消或导出器是否已 Shutdown(通过内部stopCh通道),随后将 spans 按 Resource 分组、逐个 batch 上传;Shutdown(jaeger.go)使用sync.Once关闭stopCh并调用底层 uploader 的shutdown释放连接。
两种上报端点与配置选项
导出器支持两种目标端点,各自对应不同的传输协议(见 README 的 Configuration 一节,及 uploader.go 实现):
| 端点 | 协议 | 构造选项 | 说明 |
|---|---|---|---|
| Jaeger Agent | jaeger.thriftovercompact thrift(UDP) | WithAgentEndpoint | 应用内联 sidecar/同机 Agent,低延迟、易丢包 |
| Jaeger Collector | jaeger.thriftoverHTTP | WithCollectorEndpoint | 直连 Collector,可靠传输,支持认证 |
Agent 端点选项(AgentEndpointOption)
WithAgentEndpoint默认启用 UDP 断线自动重连(AttemptReconnecting: true),其可配置选项(均实现在 uploader.go):
WithAgentHost(host string):覆盖OTEL_EXPORTER_JAEGER_AGENT_HOST,默认localhost;WithAgentPort(port string):覆盖OTEL_EXPORTER_JAEGER_AGENT_PORT,默认6831;WithLogger(*log.Logger)/WithLogr(logr.Logger):设置 Agent 客户端日志,二者互相覆盖;WithDisableAttemptReconnecting():关闭 UDP 重连客户端;WithAttemptReconnectingInterval(d time.Duration):设置重新解析 Agent 地址的间隔,默认 30 秒;WithMaxPacketSize(size int):设置最大 UDP 包大小。
Collector 端点选项(CollectorEndpointOption)
WithCollectorEndpoint支持:
WithEndpoint(url string):覆盖OTEL_EXPORTER_JAEGER_ENDPOINT,默认http://localhost:14268/api/traces;WithUsername(username string)/WithPassword(password string):覆盖同名环境变量,用于向 Collector 发送 Basic 认证;两者均无默认值,只有同时非空时才会设置Authorization头;WithHTTPClient(client *http.Client):自定义 HTTP 客户端,默认使用http.DefaultClient。
优先级规则
README 明确指出:通过选项对象(Options)进行的配置优先于环境变量。即配置解析顺序为:显式 Option > 环境变量 > 内置默认值。以WithAgentEndpoint为例,源码先以envOr(envAgentHost, "localhost")读取环境变量兜底,再依次应用传入的 Options 覆盖之(见 uploader.go)。
环境变量完整对照表
以下是 README 中给出的官方环境变量表(可同时对照 env.go 中声明的常量名):
| 环境变量 | 对应选项 | 默认值 |
|---|---|---|
OTEL_EXPORTER_JAEGER_AGENT_HOST | WithAgentHost | localhost |
OTEL_EXPORTER_JAEGER_AGENT_PORT | WithAgentPort | 6831 |
OTEL_EXPORTER_JAEGER_ENDPOINT | WithEndpoint | http://localhost:14268/api/traces |
OTEL_EXPORTER_JAEGER_USER | WithUsername | (无默认值,不设置) |
OTEL_EXPORTER_JAEGER_PASSWORD | WithPassword | (无默认值,不设置) |
源码中envOr(key, defaultValue)(env.go)的判定逻辑是:os.Getenv返回非空字符串时采用环境变量值,否则回退到默认值。这意味着空字符串环境变量会被当作未设置处理。注意OTEL_EXPORTER_JAEGER_USER与OTEL_EXPORTER_JAEGER_PASSWORD属于 Collector HTTP 端点配置,对 UDP Agent 端点无效。
典型使用方式(无需改代码,纯环境变量驱动):
export OTEL_EXPORTER_JAEGER_AGENT_HOST=jaeger-agent.default.svc.cluster.local export OTEL_EXPORTER_JAEGER_AGENT_PORT=6831 # 或直连 Collector: # export OTEL_EXPORTER_JAEGER_ENDPOINT=http://jaeger-collector:14268/api/traces源码级原理:Span 如何变成 Jaeger Batch
理解导出链路有助于排查“字段丢失”“trace 对不上”等问题。核心转换逻辑集中在 jaeger.go:
- 分组(jaegerBatchList,jaeger.go):按 Span 的 Resource 去重(
resourceKey := ss.Resource().Equivalent())聚合成多个gen.Batch,每个 Batch 携带一个Process(即资源信息)和若干 Span;空的 span 列表直接返回 nil。 - Resource → Process(process 函数,jaeger.go):遍历 Resource 属性,
service.name被特殊提取为Process.ServiceName(不再作为普通 tag),其余属性转换为 Process tags;若 Resource 中没有service.name,则用New时从默认资源取到的服务名兜底。 - Span → Thrift Span(spanToThrift,jaeger.go):
- TraceID 与 SpanID 均以
binary.BigEndian.Uint64拆分为TraceIdHigh/TraceIdLow/SpanId三个 int64 字段,ParentSpanId 取父 SpanID;时间戳与 Duration 统一以**微秒(µs,UnixNano/1000)**为单位——这解释了 Jaeger UI 中时间精度与原始纳秒的差异; - Attributes 按类型映射为 thrift Tag:
STRING→VStr、BOOL→VBool、INT64→VLong、FLOAT64→VDouble,切片类型(bool/int64/float64/string 切片)会被json.Marshal成字符串后作为VStr写入(keyValueToTag,jaeger.go#L231-L275); - 附加约定 tag:
otel.library.name/otel.library.version(来自 InstrumentationScope)、span.kind(非 Internal 时)、otel.status_code(OK/ERROR)、error=true(Error 状态时)、otel.status_description; - Events(日志)转换为
gen.Log,字段包含event(事件名,同名 attribute 会覆盖之)以及otel.event.dropped_attributes_count(被丢弃属性计数,jaeger.go#L192-L194); - Links 全部以
FOLLOWS_FROM类型的SpanRef导出(不保留CHILD_OF之外的原始关系语义)。
- TraceID 与 SpanID 均以
传输层细节:UDP 分包与 HTTP 上传
Agent(UDP + compact thrift)
agent.go 实现agentClientUDP,关键常量与行为:
udpPacketMaxLength = 65000:单个 UDP 报文最大字节数(与 jaeger-agent 同步);emitBatchOverhead = 70:数据报封装额外开销,实际可用载荷上限为65000 - 70;- 序列化采用 compact thrift 协议(
thrift.NewTCompactProtocolFactoryConf),并用TMemoryBuffer先计算每个 Span 的序列化大小; EmitBatch会做逐 span 分包:单个 Span 超过包上限则直接丢弃并记录错误;累计大小接近上限时先flush当前批次再开启新包(agent.go#L123-L178);- 默认开启重连 UDP 客户端(
newReconnectingUDPConn,位于 reconnecting_udp_client.go),会在 Agent 主机名对应的 DNS 记录变化时自动重新解析连接,重连间隔默认 30 秒。
Collector(HTTP + binary thrift)
uploader.go 中collectorUploader.upload的行为:
- 使用binary thrift协议序列化整个 Batch(
thrift.NewTBinaryProtocolConf),与 Agent 端的 compact 协议不同; - 发起
POST请求到配置的 Endpoint,Content-Type固定为application/x-thrift; - 仅当 username 与 password同时非空时才调用
req.SetBasicAuth设置 Basic 认证; - 响应体被读取并丢弃(
io.Copy(io.Discard, resp.Body)),只要 HTTP 状态码不在[200, 300)区间即返回failed to upload traces; HTTP status code: %d错误; shutdown为空操作——HTTP 无长连接需要关闭。
从 Jaeger Exporter 迁移到 OTLP
鉴于模块已弃用,README 给出的官方迁移方向是改用 OTLP Trace Exporter:
otlptracehttp:go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp,基于 HTTP/protobuf 上报,适合直连 Collector 或网关;otlptracegrpc:go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc,基于 gRPC 上报。
迁移时需同步调整 Jaeger 侧配置(启用 OTLP 接收端口),并将原有jaeger.New(jaeger.WithAgentEndpoint(...))等调用替换为 OTLP 对应的New/WithEndpoint选项;环境变量OTEL_EXPORTER_JAEGER_*也应替换为 OTLP 标准变量(如OTEL_EXPORTER_OTLP_ENDPOINT)。当前仓库的 vendor 依赖中已包含 OTLP 相关模块,可参照 vendor 目录确认可用的 OTLP exporter 版本后平滑切换。
参考资料与源码导航
- 模块官方文档:vendor/go.opentelemetry.io/otel/exporters/jaeger/README.md
- 入口与 Span 转换:vendor/go.opentelemetry.io/otel/exporters/jaeger/jaeger.go
- 环境变量定义:vendor/go.opentelemetry.io/otel/exporters/jaeger/env.go
- UDP Agent 客户端:vendor/go.opentelemetry.io/otel/exporters/jaeger/agent.go、vendor/go.opentelemetry.io/otel/exporters/jaeger/reconnecting_udp_client.go
- 端点选项与 HTTP 上传:vendor/go.opentelemetry.io/otel/exporters/jaeger/uploader.go
- Thrift 生成代码:vendor/go.opentelemetry.io/otel/exporters/jaeger/internal/gen-go/jaeger、vendor/go.opentelemetry.io/otel/exporters/jaeger/internal/gen-go/agent
- SDK 装配参考:pkg/xcap/tracer_test.go(展示 TracerProvider + SpanProcessor 的标准用法)
【免费下载链接】lokiLike Prometheus, but for logs.项目地址: https://gitcode.com/GitHub_Trending/lok/loki
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考