Go 语言使用 loongsuite-go 上报观测云最佳实践

本文详解 OpenTelemetry 标准协议接入观测云 DataKit 实操,零码无侵入搭建可观测体系,实现日志、调用链路联动分析。

最佳实践
banner.png

背景

Go 应用接入链路追踪时,常见方案是在代码中手动引入 OpenTelemetry SDK,并为 HTTP、gRPC、数据库、Redis、MongoDB 等组件逐一添加 instrumentation。这种方式可控性强,但对已有项目改造成本较高,也容易出现埋点不完整、上下文传播遗漏、依赖版本不兼容等问题。

loongsuite-go 是阿里巴巴开源的 Go 编译时自动插桩工具。它在 go build 阶段将 OpenTelemetry 埋点逻辑注入到应用中,应用运行后通过标准 OTEL 协议上报链路数据。对于希望快速把 Go 应用接入观测云的场景,loongsuite-go 的优势是:

  • 对业务代码侵入小,HTTP、gRPC、MySQL、Redis、MongoDB 等常见组件可以通过编译时插桩自动生成 span。
  • 使用 OpenTelemetry 标准协议,上报端可以直接对接观测云 DataKit 的 OTEL 接收端。
  • 适合容器化交付,把 otel 编译工具放进构建镜像,运行镜像只保留最终二进制。
  • 可以结合 JSON 日志,把 trace_idspan_id 写入日志字段,便于在观测云中实现日志和链路关联。

本文以 go-demo1go-demo2 为例:

  • go-demo1:HTTP 服务,提供 POST /createOrder,通过 gRPC 调用 go-demo2
  • go-demo2:gRPC 服务,处理支付逻辑,并调用 MySQL、Redis、MongoDB。
  • 两个服务均使用 loongsuite-go 编译时插桩。
  • 链路通过 OTEL gRPC 上报到主机上的 DataKit:192.168.0.245:4317

前置条件

1. 使用 loongsuite-go 编译时插桩

loongsuite-go 的关键不是运行时代理,而是构建阶段使用:

otel go build -o app .

当前项目为避免构建时远程下载失败,提前把 otel-linux-amd64install.sh 文件放在项目根目录。

install.sh 只负责把本地 otel-linux-amd64 安装到 /usr/local/bin/otel

#!/bin/bash
set -e

install() {
    SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
    EXECUTABLE="${1:-${SCRIPT_DIR}/otel-linux-amd64}"
    INSTALL_DIR="${INSTALL_DIR:-/usr/local/bin}"

    if [ ! -f "$EXECUTABLE" ]; then
        echo "Executable $EXECUTABLE not found"
        exit 1
    fi

    echo "Installing $EXECUTABLE to $INSTALL_DIR"
    mkdir -p "$INSTALL_DIR"
    cp "$EXECUTABLE" "$INSTALL_DIR/otel"
    chmod +x "$INSTALL_DIR/otel"
}

install "$@"

这样 Docker 构建时不会再访问 GitHub 下载 otel 工具,适合网络不稳定或内网环境。

2. Dockerfile 推荐写法

go-demo2 为例:

FROM golang:1.26 AS builder

WORKDIR /src
ARG GOPROXY=https://goproxy.cn,direct

COPY go-demo2/go.mod ./
RUN --mount=type=cache,target=/go/pkg/mod \
    GOPROXY="${GOPROXY}" go mod download

COPY install.sh otel-linux-amd64 /tmp/
RUN chmod +x /tmp/install.sh && /tmp/install.sh /tmp/otel-linux-amd64

COPY go-demo2/ .
RUN --mount=type=cache,target=/go/pkg/mod \
    --mount=type=cache,target=/root/.cache/go-build \
    GOPROXY="${GOPROXY}" OTELTOOL_VERBOSE=true otel go build -o /out/go-demo2 .

FROM debian:bookworm-slim

WORKDIR /app
COPY --from=builder /out/go-demo2 /app/go-demo2

EXPOSE 9090
ENTRYPOINT ["/app/go-demo2"]

实践建议:

  • 构建阶段使用 golang 镜像,运行阶段使用更小的 debian:bookworm-slim
  • go mod downloadgo build 使用 BuildKit cache,加快重复构建。
  • 使用 OTELTOOL_VERBOSE=true,便于确认哪些规则参与了插桩。
  • 生产环境建议固定 Go 版本、依赖版本和 otel-linux-amd64 版本,避免构建结果漂移。

3. 配置服务名和上报地址

每个服务必须设置独立的 OTEL_SERVICE_NAME,否则观测云中服务名会混乱。

  OTEL_SERVICE_NAME: "go-demo2"
  OTEL_EXPORTER_OTLP_ENDPOINT: "http://192.168.0.245:4317"
  OTEL_EXPORTER_OTLP_INSECURE: true
  OTEL_EXPORTER_OTLP_PROTOCOL: "grpc"
  OTEL_EXPORTER_OTLP_TRACES_PROTOCOL: "grpc"
  OTEL_TRACE_SAMPLER: "1.0"
    - name: OTEL_SERVICE_NAME
      value: xxxx
    - name: OTEL_EXPORTER_OTLP_ENDPOINT
      value: http://datakit-service.datakit:4317
    - name: OTEL_EXPORTER_OTLP_PROTOCOL
      value: grpc

4. Json日志与 trace_id 关联

loongsuite-go 可能会对日志做自动 trace 注入,输出类似:

trace_id=... span_id=...order_id=...

如果应用使用 JSON 日志,不建议依赖这种文本前缀式注入,因为它可能破坏 JSON 格式。更推荐在日志代码中从 context.Context 获取当前 span,再把 trace_idspan_id 写成 JSON 字段。

示例:

import (
    "context"
    "log/slog"
    "os"

    "go.opentelemetry.io/otel/trace"
)

var logger = slog.New(slog.NewJSONHandler(os.Stdout, nil))

func logAttrs(ctx context.Context, level slog.Level, message string, attrs ...slog.Attr) {
    spanContext := trace.SpanContextFromContext(ctx)
    if spanContext.IsValid() {
        attrs = append(attrs,
            slog.String("trace_id", spanContext.TraceID().String()),
            slog.String("span_id", spanContext.SpanID().String()),
        )
    }
    logger.LogAttrs(ctx, level, message, attrs...)
}

业务日志:

logAttrs(
    ctx,
    slog.LevelInfo,
    "支付完成",
    slog.String("order_id", req.OrderID),
    slog.String("pay_id", payID),
)

输出示例:

{
  "time": "2026-07-20 02:32:38.927",
  "level": "INFO",
  "msg": "支付完成",
  "order_id": "order-10001",
  "pay_id": "pay-82612a021dc38219",
  "trace_id": "d28d5c26510ce9d62827e5c3dcab2786",
  "span_id": "f73228669d3ccb53"
}

5. Pipeline

登录 观测云控制台,点击「日志」 -「Pipelines」 -「新建Pipeline」,按下图配置Pipeline。

解析规则内容如下:

json_data = load_json(_)
add_key(time,json_data["time"])
add_key(status,json_data["level"])
add_key(trace_id,json_data["trace_id"])
add_key(span_id,json_data["span_id"])
default_time(time,"Asia/Shanghai")

6. 效果展示

总结

Go 应用使用 loongsuite-go 接入观测云时,推荐采用以下实践:

  • 使用 otel go build 做编译时自动插桩。
  • 通过 OTEL gRPC 上报到 DataKit。
  • 每个服务明确设置 OTEL_SERVICE_NAME
  • 容器内上报地址使用可达的宿主机 IP,而不是 127.0.0.1
  • 日志使用 JSON 格式,并把 trace_idspan_id 作为结构化字段输出。
  • 构建阶段使用本地 otel-linux-amd64,减少外网依赖,提高构建稳定性。

参考:

获取专属方案

联系我们

加入社区

微信扫码
加入官方交流群

立即体验

在线开通,按量计费,真正的云服务!

立即开始

选择观测云版本

代码托管平台