以 Go 服务为例,讲解 OpenTelemetry 接入炬鲸 OBSERVE 的完整流程:环境准备、SDK 初始化、自动与手动埋点、OTLP 上报,以及采样率、时钟漂移等常见坑。
想要看全链路的调用关系,光靠日志不够,需要把 trace 采进来。OpenTelemetry(OTel)是目前事实上的埋点标准,接入炬鲸 OBSERVE 只需要三步:装 SDK、配置 OTLP 上报、在控制台确认数据落库。下面以 Go 服务为例走一遍完整流程。
接入前确认三件事:
service.name 命名规范,比如 {业务域}-{服务名},避免后续在 trace 检索里对不上号。以 Go 为例,引入 OTel SDK 和 gRPC exporter:
import (
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/sdk/trace"
"go.opentelemetry.io/otel/sdk/resource"
semconv "go.opentelemetry.io/otel/semconv/v1.17.0"
)
func initTracer(ctx context.Context) (*trace.TracerProvider, error) {
exporter, err := otlptracegrpc.New(ctx,
otlptracegrpc.WithEndpoint("ob.jjhub.cn:4317"),
otlptracegrpc.WithInsecure(),
)
if err != nil {
return nil, err
}
res := resource.NewWithAttributes(semconv.SchemaURL,
semconv.ServiceName("order-service"),
)
tp := trace.NewTracerProvider(
trace.WithBatcher(exporter),
trace.WithResource(res),
)
otel.SetTracerProvider(tp)
return tp, nil
}
两个关键点:ServiceName 会作为炬鲸里区分服务的维度,务必每个服务填准确;WithBatcher 批量上报,比逐条上报省一半网络开销。
生产环境务必换成 TLS(otlptracegrpc.WithTLSCredentials(...)),WithInsecure 只适合内网测试。OTLP 走的是长连接,跨机房部署时防火墙要放行持续连接,而不是只放行一次握手。
HTTP 服务优先用自动埋点,改一行路由注册即可:
import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
mux.Handle("/api/order", otelhttp.NewHandler(orderHandler, "place-order"))
自动埋点能拿到"收到请求 → 返回响应"这个 span,但业务内部的耗时(查库、调下游、计算)要靠手动埋点补:
ctx, span := otel.Tracer("order-service").Start(ctx, "query-inventory")
defer span.End()
rows, err := db.QueryContext(ctx, "SELECT stock FROM inventory WHERE sku = ?", sku)
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
}
记得在业务错误分支调用 span.RecordError 并设置 codes.Error,否则错误 span 在 trace 视图里看不出红色标记,漏掉大量真实故障。
给 span 加业务属性是排障的关键,别只起个名字就完事。把订单号、SKU、地区这类业务标识用 span.SetAttributes 放进去,检索时才能按业务维度过滤。但要注意别放高基数属性——属性会随 span 落盘,基数过高会拖慢查询。
炬鲸控制台「接入中心 → OpenTelemetry」里生成一个接入 token,把它作为 header 带上,用于区分租户:
exporter, _ := otlptracegrpc.New(ctx,
otlptracegrpc.WithEndpoint("ob.jjhub.cn:4317"),
otlptracegrpc.WithHeaders(map[string]string{
"Authorization": "Bearer " + token,
}),
)
启动服务后压几条请求,到控制台「链路追踪 → Trace 检索」,按服务名或 trace_id 查询。能看到完整的父子 span 瀑布图、每个 span 的耗时占比,以及右侧关联的日志和告警。如果 5 分钟后还查不到,优先检查 exporter 的报错日志(通常是连接被拒或 token 失效)。
另一个常见问题是资源未关联:service.name 拼写不一致会把 trace 归到两个不同的服务名下,检索前先核对命名。
trace.WithSampler(trace.ParentBased(trace.TraceIDRatioBased(0.1))) 采样 10%,等数据量稳定再上调。span.SetAttributes(attribute.String("user_id", uid)) 这类值不要在所有 span 上无差别塞入,用户 ID 建议只在关键交易 span 上设置,否则会推高索引体积、拖慢 trace 检索。trace 解决的是"这次调用慢在哪",指标解决的是"整体有没有异常"。建议同一套 OTel SDK 把 metrics 也上报:Go 里用 go.opentelemetry.io/otel/sdk/metric 初始化 MeterProvider,配合 runtime 采集器上报 GC、协程数、内存等运行时指标。这样服务刚接入就有基础性能基线,trace 里发现慢查询时,也能在指标面板上判断这是个别问题还是全局性劣化。
接入完成后,把 trace_id 打印进日志(log.Info("...", "trace_id", span.SpanContext().TraceID())),日志和 trace 就能互相串联,排障时从日志一键跳到对应链路。日志-指标-trace 三者齐了,才算真正用上可观测性。