这里使用 app -> collector-contrib
进行转发, 应用不直接对后端的存储。 适配性 更高。
collector-contrib
最常见的两种协议 grpc / http(s)
。 传入 endpoint 地址进行初始化 Provider, 参考代码 grpcExporter 和 httpExporter
- 2. 使用 otelgin 接入 trace.Provider
-
第一步初始化好的 trace.Provider 需要通过 Option 的方式传入, 参考代码 otel middleware option
-
在 gin 已经实现了一个官方的 Middleware 支持 OpenTelemetry. 参考代码 gin-gonic/gin/otelgin
-
在 #L66 中, 使用
c.Set(k,v)
将 provider 放入了 gin 自己实现的 Context 中。 -
在 #L88-92 中,
tracer.Start
启动了第一个 Span, 并将生成的 ctx 放入 Request 中向下传递。 之后我们将从 Request 中取 tracer provider。 -
在 #L73,98, 使用
httpconv.XXXXX
方法进行 span 状态设置。httpconv
是一个 OpenTelemetry 实现的 标准/模版 方法, 用于处理 http 请求中的各种情况。 可以多跟一下。 -
在 #L71-87 中, 初始化了一些状态。
在使用的时候, 需要使用 Context 在不同的 函数/方法 之间传递 Provider。 每个 函数/方法 创建自己的 Span, 以此实现 调用的父子关系。
- 在 utils/span.go 中, 封装了一个函数
Span(xxxx)
提出 context 中的 provider 并启动tracer.Start(xxx)
。
在 #L21 中, 对 ctx
进行了判断, 如果 ctx 是 gin.Context
的话, 就需要从 Request 中携带的 context, 这一点在上诉的 2.4. 中已经说明原因。
额外的进行了一些 公共属性 的设置, 例如运行的主机名。
- 在 apis/user/info.go 中, 通过 context 在不同 函数/方法 之间传递 tracer provider, 每个地方都调用了
Span(xxx)
跟踪当前情况。
当有需求的时候(例如出现访问错误), 需要把 TraceID 返回给用户。 这样用户在报错的时候提供 TraceID 可以快速 debug。
在 otel/response_traceid.go 创建了一个 Gin Middleware, 将 TraceID 从 Context 中提取出来, 并放到 Response Header 中。
其中用到了 propagation
标准库, 简单快捷。
在上图 App2 中, 能够拿到 App 传递的 Traceparent header, 这样就保证了接收侧的 TraceID 连贯性。
- 在
otelgin
中, 提供了一个 Option 注入, otel/propagation, 使用otelgin.WithPropagators(pptc)
- 在 gin 中注册 provider 的时候, 使用 Option 即可, otel/register.go#L12
为了保证 TraceID 的连贯性, 除了接收侧(App2)。 在 发送侧 App1 也需要做对应的操作。
从 Context 中读取 TraceParent 并注入到 HTTP Request Header 中。
- 在 utils/carrier.go#L9 中, 通过
propagation
标准库将 Header 字段找出来。 - 在 httpclient/client.go#L19 中, 将找到的 Header 字段全部放到新创建的 Request Header 中。
标准 API 用法。
span.RecordError
提交错误日志span.SetStatus
设置 trace span 状态。 氛围 error 和 okspan.SetAttributes
设置属性,可以通过属性搜索。 (所有属性被 索引)。
在 Tracer 启动的时候传入。 启动之后 Span 不能设置。 可以通过 Kind 类型, 表明当前步骤类型, 以后在 检索/查询 的时候更直观。
- (*) Kind 是标准字段, 是枚举类型。 其中包含
internal, server, client, producer, consumer
可以在代码中看到。 - 可以通过
trace.WithSpanKind
, 在trace.Start
时作为 opt 传入。 之后不能通过 span 设置。
- (*) 自定义字段(Attribute)(类似 host).
- 每个 span 都是独立的。 因此 public attributes 需要在公共函数中注入 utils/span.go
- 因此使用 Context 进行传递, 在不同的 方法/函数 内进行公共 attr 共享。 (看自己情况实现)
nginx/web -> app1----> app2(get balance) -----> app3 (check db)
\
\-> app4(get cellphone) ----> app5 (check redis)
正常图示
有 Error 图示