本页面将向你展示如何在 Go 中开始使用 OpenTelemetry。
你将学习如何手动为一个简单的应用程序进行插桩, 并且使其能将 traces,metrics 和 logs 输出到终端。
日志信号仍处于实验阶段,在未来版本可能会引入不兼容的更改。
前置条件{#prerequisites}
确保你本地已经安装了:
- Go 1.22 及更高版本。
示例应用程序{#example-application}
以下示例使用了一个基本的 net/http 应用程序。
如果你没有使用 net/http 也没关系 —— 你仍然可以在其他 Web 框架中使用
OpenTelemetry Go,比如 Gin 和 Echo,有关支持框架的完整库列表,请参阅
registry。
如果需要更复杂的示例,请参阅 examples。
开始{#setup}
首先,需要在新的目录中设置 go.mod:
go mod init dice
创建并启动 HTTP 服务器{#create-and-launch-an-http-server}
在同一个文件夹中,创建一个 main.go 文件,并添加以下代码到这个文件中:
package main
import (
"log"
"net/http"
)
func main() {
http.HandleFunc("/rolldice", rolldice)
log.Fatal(http.ListenAndServe(":8080", nil))
}
创建另一个叫做 rolldice.go 的文件,并添加以下代码到该文件。
package main
import (
"io"
"log"
"math/rand"
"net/http"
"strconv"
)
func rolldice(w http.ResponseWriter, r *http.Request) {
roll := 1 + rand.Intn(6)
resp := strconv.Itoa(roll) + "\n"
if _, err := io.WriteString(w, resp); err != nil {
log.Printf("Write failed: %v\n", err)
}
}
通过以下命令构建并运行应用程序。
go run .
在你的浏览器中打开 http://localhost:8080/rolldice 确保应用程序正常工作。
添加 OpenTelemetry 插桩{#add-opentelemetry-instrumentation}
现在我们将展示如何向示例应用程序添加 OpenTelemetry 插桩。如果你正在使用自己的应用程序,也可以按照步骤操作,只需要注意你的代码可能会略有不同。
添加依赖项{#add-dependencies}
安装以下软件包:
go get "go.opentelemetry.io/otel" \
"go.opentelemetry.io/otel/exporters/stdout/stdoutmetric" \
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace" \
"go.opentelemetry.io/otel/exporters/stdout/stdoutlog" \
"go.opentelemetry.io/otel/sdk/log" \
"go.opentelemetry.io/otel/log/global" \
"go.opentelemetry.io/otel/propagation" \
"go.opentelemetry.io/otel/sdk/metric" \
"go.opentelemetry.io/otel/sdk/resource" \
"go.opentelemetry.io/otel/sdk/trace" \
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"\
"go.opentelemetry.io/contrib/bridges/otelslog"
这将安装 OpenTelemetry SDK 组件以及 net/http 的插桩库。
如果你要为其他网络请求库进行插桩,需要安装对应的插桩库,更多信息请查看
libraries。
初始化 OpenTelemetry SDK{#initialize-the-opentelemetry-sdk}
首先我们需要初始化 OpenTelemetry SDK。这对任何需要导出遥测数据的应用程序都是必须的
创建一个 otel.go 文件,并在其中编写 OpenTelemetry SDK 的初始化代码:
package main
import (
"context"
"errors"
"time"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/stdout/stdoutlog"
"go.opentelemetry.io/otel/exporters/stdout/stdoutmetric"
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
"go.opentelemetry.io/otel/log/global"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/sdk/log"
"go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/trace"
)
// setupOTelSDK 初始化 OpenTelemetry 的管道。
// 如果没有返回错误, 用户需要确保在之后调用返回的 shutdown 方法进行清理。
func setupOTelSDK(ctx context.Context) (func(context.Context) error, error) {
var shutdownFuncs []func(context.Context) error
var err error
// shutdown 会调用所有注册的清理函数。
// 所有返回的错误都会被合并到一起。
// 每个注册的清理函数仅会被调用一次。
shutdown := func(ctx context.Context) error {
var err error
for _, fn := range shutdownFuncs {
err = errors.Join(err, fn(ctx))
}
shutdownFuncs = nil
return err
}
// handleErr 用户调用 shutdown 并合并返回的错误。
handleErr := func(inErr error) {
err = errors.Join(inErr, shutdown(ctx))
}
// 设置上下文传播器(用于跨服务传递追踪信息)。
prop := newPropagator()
otel.SetTextMapPropagator(prop)
// 初始化 trace 提供者。
tracerProvider, err := newTracerProvider()
if err != nil {
handleErr(err)
return shutdown, err
}
shutdownFuncs = append(shutdownFuncs, tracerProvider.Shutdown)
otel.SetTracerProvider(tracerProvider)
// 初始化 Meter 提供者。
meterProvider, err := newMeterProvider()
if err != nil {
handleErr(err)
return shutdown, err
}
shutdownFuncs = append(shutdownFuncs, meterProvider.Shutdown)
otel.SetMeterProvider(meterProvider)
// 初始化日志提供者。
loggerProvider, err := newLoggerProvider()
if err != nil {
handleErr(err)
return
}
shutdownFuncs = append(shutdownFuncs, loggerProvider.Shutdown)
global.SetLoggerProvider(loggerProvider)
return shutdown, err
}
func newPropagator() propagation.TextMapPropagator {
return propagation.NewCompositeTextMapPropagator(
propagation.TraceContext{},
propagation.Baggage{},
)
}
func newTracerProvider() (*trace.TracerProvider, error) {
traceExporter, err := stdouttrace.New(
stdouttrace.WithPrettyPrint())
if err != nil {
return nil, err
}
tracerProvider := trace.NewTracerProvider(
trace.WithBatcher(traceExporter,
// 默认批处理时间为 5 秒,这里设置为 1 秒用于演示。
trace.WithBatchTimeout(time.Second)),
)
return tracerProvider, nil
}
func newMeterProvider() (*metric.MeterProvider, error) {
metricExporter, err := stdoutmetric.New()
if err != nil {
return nil, err
}
meterProvider := metric.NewMeterProvider(
metric.WithReader(metric.NewPeriodicReader(metricExporter,
// 默认采集间隔为 1 分钟,这里设置为 3 秒用于演示。
metric.WithInterval(3*time.Second))),
)
return meterProvider, nil
}
func newLoggerProvider() (*log.LoggerProvider, error) {
logExporter, err := stdoutlog.New()
if err != nil {
return nil, err
}
loggerProvider := log.NewLoggerProvider(
log.WithProcessor(log.NewBatchProcessor(logExporter)),
)
return loggerProvider, nil
}
如果你只使用链路追踪(tracing)或者指标(metrics),你可以忽略对应的 TracerProvider 或者 MeterProvider 的初始化代码。
为 HTTP 服务器添加插桩{#instrument-the-http-server}
现在我们已经为 OpenTelemetry SDK 完成了初始化,可以为 HTTP 服务器添加插桩了。
修改 main.go 文件,添加设置并初始化 OpenTelemetry SDK 的代码,并使用 otelhttp 插桩库对 HTTP 服务器进行插桩处理:
package main
import (
"context"
"errors"
"log"
"net"
"net/http"
"os"
"os/signal"
"time"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
)
func main() {
if err := run(); err != nil {
log.Fatalln(err)
}
}
func run() error {
// 优雅地处理中断信号(Ctrl+C)。
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
defer stop()
// 设置并初始化 OpenTelemetry SDK。
otelShutdown, err := setupOTelSDK(ctx)
if err != nil {
return err
}
// 确保在程序结束之前调用 shutdown 方法清理资源。
defer func() {
err = errors.Join(err, otelShutdown(context.Background()))
}()
// 启动 HTTP 服务器。
srv := &http.Server{
Addr: ":8080",
BaseContext: func(_ net.Listener) context.Context { return ctx },
ReadTimeout: time.Second,
WriteTimeout: 10 * time.Second,
Handler: newHTTPHandler(),
}
srvErr := make(chan error, 1)
go func() {
srvErr <- srv.ListenAndServe()
}()
// 等待中断信号
select {
case err = <-srvErr:
// 启动 HTTP 服务器时出错。
return err
case <-ctx.Done():
// 等待第一个 CTRL+C 信号。
// 尽快停止接受信号通知。
stop()
}
// 当调用 Shutdown 时,ListenAndServe 会立即返回 ErrServerClosed 错误。
err = srv.Shutdown(context.Background())
return err
}
func newHTTPHandler() http.Handler {
mux := http.NewServeMux()
// 注册 Handler。
mux.Handle("/rolldice/", rolldice)
mux.Handle("/rolldice/{player}", rolldice)
// 为整个服务器添加 HTTP 插桩处理器。
handler := otelhttp.NewHandler(mux, "/")
return handler
}
添加自定义插桩器(Instrumentation){#add-custom-instrumentation}
插桩库用于捕获系统边缘的遥测数据,比如传入和传出的 HTTP 请求,但是他们没办法捕获应用程序内部的执行情况。为此,你需要编写一些自定义手动插桩.
修改 rolldice.go 文件,使用 OpenTelemetry API 添加自定义插桩逻辑:
package main
import (
"fmt"
"io"
"log"
"math/rand"
"net/http"
"strconv"
"go.opentelemetry.io/contrib/bridges/otelslog"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric"
)
const name = "go.opentelemetry.io/otel/example/dice"
var (
tracer = otel.Tracer(name)
meter = otel.Meter(name)
logger = otelslog.NewLogger(name)
rollCnt metric.Int64Counter
)
func init() {
var err error
// rollCnt 是自定义的需要捕捉的指标对象。
rollCnt, err = meter.Int64Counter("dice.rolls",
metric.WithDescription("The number of rolls by roll value"),
metric.WithUnit("{roll}"))
if err != nil {
panic(err)
}
}
func rolldice(w http.ResponseWriter, r *http.Request) {
ctx, span := tracer.Start(r.Context(), "roll")
defer span.End()
roll := 1 + rand.Intn(6)
var msg string
if player := r.PathValue("player"); player != "" {
msg = fmt.Sprintf("%s is rolling the dice", player)
} else {
msg = "Anonymous player is rolling the dice"
}
logger.InfoContext(ctx, msg, "result", roll)
// 为这个指标定义一个属性,表示是骰子点数值。
rollValueAttr := attribute.Int("roll.value", roll)
span.SetAttributes(rollValueAttr)
rollCnt.Add(ctx, 1, metric.WithAttributes(rollValueAttr))
resp := strconv.Itoa(roll) + "\n"
if _, err := io.WriteString(w, resp); err != nil {
log.Printf("Write failed: %v\n", err)
}
}
请注意,如果你只使用链路追踪(tracing)或指标(metrics),可以忽略对应的其他遥测类型的插桩代码。
运行应用程序{#run-the-application}
使用以下命令构建并运行应用程序:
go mod tidy
export OTEL_RESOURCE_ATTRIBUTES="service.name=dice,service.version=0.1.0"
go run .
在你的浏览器打开 http://localhost:8080/rolldice/Alice。当你向服务器发送请求的时候,控制台将会输出两个 span。一个是由插桩库生成的 span,用于追踪对 /rolldice/{player} 路由的请求生命周期。另一个名为 roll 的 span 是手动创建的,他是之前提到的 span 的子 span。
查看示例输出
{
"Name": "roll",
"SpanContext": {
"TraceID": "829fb7ceb787403c96eac3caf285c965",
"SpanID": "8b6b408b6c1a35e5",
"TraceFlags": "01",
"TraceState": "",
"Remote": false
},
"Parent": {
"TraceID": "829fb7ceb787403c96eac3caf285c965",
"SpanID": "612be4bbdf450de6",
"TraceFlags": "01",
"TraceState": "",
"Remote": false
},
"SpanKind": 1,
"StartTime": "2023-09-25T12:42:06.177119576+02:00",
"EndTime": "2023-09-25T12:42:06.177136776+02:00",
"Attributes": [
{
"Key": "roll.value",
"Value": {
"Type": "INT64",
"Value": 6
}
}
],
"Events": null,
"Links": null,
"Status": {
"Code": "Unset",
"Description": ""
},
"DroppedAttributes": 0,
"DroppedEvents": 0,
"DroppedLinks": 0,
"ChildSpanCount": 0,
"Resource": [
{
"Key": "service.name",
"Value": {
"Type": "STRING",
"Value": "dice"
}
},
{
"Key": "service.version",
"Value": {
"Type": "STRING",
"Value": "0.1.0"
}
},
{
"Key": "telemetry.sdk.language",
"Value": {
"Type": "STRING",
"Value": "go"
}
},
{
"Key": "telemetry.sdk.name",
"Value": {
"Type": "STRING",
"Value": "opentelemetry"
}
},
{
"Key": "telemetry.sdk.version",
"Value": {
"Type": "STRING",
"Value": "1.19.0-rc.1"
}
}
],
"InstrumentationLibrary": {
"Name": "rolldice",
"Version": "",
"SchemaURL": ""
}
}
{
"Name": "/",
"SpanContext": {
"TraceID": "829fb7ceb787403c96eac3caf285c965",
"SpanID": "612be4bbdf450de6",
"TraceFlags": "01",
"TraceState": "",
"Remote": false
},
"Parent": {
"TraceID": "00000000000000000000000000000000",
"SpanID": "0000000000000000",
"TraceFlags": "00",
"TraceState": "",
"Remote": false
},
"SpanKind": 2,
"StartTime": "2023-09-25T12:42:06.177071077+02:00",
"EndTime": "2023-09-25T12:42:06.177158076+02:00",
"Attributes": [
{
"Key": "http.method",
"Value": {
"Type": "STRING",
"Value": "GET"
}
},
{
"Key": "http.scheme",
"Value": {
"Type": "STRING",
"Value": "http"
}
},
{
"Key": "http.flavor",
"Value": {
"Type": "STRING",
"Value": "1.1"
}
},
{
"Key": "net.host.name",
"Value": {
"Type": "STRING",
"Value": "localhost"
}
},
{
"Key": "net.host.port",
"Value": {
"Type": "INT64",
"Value": 8080
}
},
{
"Key": "net.sock.peer.addr",
"Value": {
"Type": "STRING",
"Value": "::1"
}
},
{
"Key": "net.sock.peer.port",
"Value": {
"Type": "INT64",
"Value": 49046
}
},
{
"Key": "http.user_agent",
"Value": {
"Type": "STRING",
"Value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36"
}
},
{
"Key": "http.route",
"Value": {
"Type": "STRING",
"Value": "/rolldice/Alice"
}
},
{
"Key": "http.wrote_bytes",
"Value": {
"Type": "INT64",
"Value": 2
}
},
{
"Key": "http.status_code",
"Value": {
"Type": "INT64",
"Value": 200
}
}
],
"Events": null,
"Links": null,
"Status": {
"Code": "Unset",
"Description": ""
},
"DroppedAttributes": 0,
"DroppedEvents": 0,
"DroppedLinks": 0,
"ChildSpanCount": 1,
"Resource": [
{
"Key": "service.name",
"Value": {
"Type": "STRING",
"Value": "dice"
}
},
{
"Key": "service.version",
"Value": {
"Type": "STRING",
"Value": "0.1.0"
}
},
{
"Key": "telemetry.sdk.language",
"Value": {
"Type": "STRING",
"Value": "go"
}
},
{
"Key": "telemetry.sdk.name",
"Value": {
"Type": "STRING",
"Value": "opentelemetry"
}
},
{
"Key": "telemetry.sdk.version",
"Value": {
"Type": "STRING",
"Value": "1.19.0-rc.1"
}
}
],
"InstrumentationLibrary": {
"Name": "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp",
"Version": "0.44.0",
"SchemaURL": ""
}
}
除了追踪信息之外,日志信息也会被输出到控制台。
查看示例输出
{
"Timestamp": "2023-09-25T12:42:05.177136776+02:00",
"ObservedTimestamp": "2023-09-25T12:42:06.809396011+02:00",
"Severity": 9,
"SeverityText": "",
"Body": {
"Type": "String",
"Value": "Alice is rolling the dice"
},
"Attributes": [
{
"Key": "result",
"Value": {
"Type": "Int64",
"Value": 6
}
}
],
"TraceID": "829fb7ceb787403c96eac3caf285c965",
"SpanID": "8b6b408b6c1a35e5",
"TraceFlags": "01",
"Resource": [
{
"Key": "service.name",
"Value": {
"Type": "STRING",
"Value": "dice"
}
},
{
"Key": "service.version",
"Value": {
"Type": "STRING",
"Value": "0.1.0"
}
},
{
"Key": "telemetry.sdk.language",
"Value": {
"Type": "STRING",
"Value": "go"
}
},
{
"Key": "telemetry.sdk.name",
"Value": {
"Type": "STRING",
"Value": "opentelemetry"
}
},
{
"Key": "telemetry.sdk.version",
"Value": {
"Type": "STRING",
"Value": "1.19.0-rc.1"
}
}
],
"Scope": {
"Name": "rolldice",
"Version": "",
"SchemaURL": ""
},
"DroppedAttributes": 0
}
多刷新几次 http://localhost:8080/rolldice/Alice 页面,然后等待一会或者终止你的应用程序,随后你会在控制台输出中看到指标信息。你会看到 dice.rolls 指标被打印到控制台,其中每个骰子都有单独的计数,同时还会看到由插桩库生成的 HTTP 请求相关指标。
查看示例输出
{
"Resource": [
{
"Key": "service.name",
"Value": {
"Type": "STRING",
"Value": "dice"
}
},
{
"Key": "service.version",
"Value": {
"Type": "STRING",
"Value": "0.1.0"
}
},
{
"Key": "telemetry.sdk.language",
"Value": {
"Type": "STRING",
"Value": "go"
}
},
{
"Key": "telemetry.sdk.name",
"Value": {
"Type": "STRING",
"Value": "opentelemetry"
}
},
{
"Key": "telemetry.sdk.version",
"Value": {
"Type": "STRING",
"Value": "1.19.0-rc.1"
}
}
],
"ScopeMetrics": [
{
"Scope": {
"Name": "rolldice",
"Version": "",
"SchemaURL": ""
},
"Metrics": [
{
"Name": "dice.rolls",
"Description": "The number of rolls by roll value",
"Unit": "{roll}",
"Data": {
"DataPoints": [
{
"Attributes": [
{
"Key": "roll.value",
"Value": {
"Type": "INT64",
"Value": 1
}
}
],
"StartTime": "2023-09-25T12:42:04.279204638+02:00",
"Time": "2023-09-25T12:42:15.482694258+02:00",
"Value": 4
},
{
"Attributes": [
{
"Key": "roll.value",
"Value": {
"Type": "INT64",
"Value": 5
}
}
],
"StartTime": "2023-09-25T12:42:04.279204638+02:00",
"Time": "2023-09-25T12:42:15.482694258+02:00",
"Value": 3
},
{
"Attributes": [
{
"Key": "roll.value",
"Value": {
"Type": "INT64",
"Value": 3
}
}
],
"StartTime": "2023-09-25T12:42:04.279204638+02:00",
"Time": "2023-09-25T12:42:15.482694258+02:00",
"Value": 4
},
{
"Attributes": [
{
"Key": "roll.value",
"Value": {
"Type": "INT64",
"Value": 2
}
}
],
"StartTime": "2023-09-25T12:42:04.279204638+02:00",
"Time": "2023-09-25T12:42:15.482694258+02:00",
"Value": 2
},
{
"Attributes": [
{
"Key": "roll.value",
"Value": {
"Type": "INT64",
"Value": 6
}
}
],
"StartTime": "2023-09-25T12:42:04.279204638+02:00",
"Time": "2023-09-25T12:42:15.482694258+02:00",
"Value": 5
},
{
"Attributes": [
{
"Key": "roll.value",
"Value": {
"Type": "INT64",
"Value": 4
}
}
],
"StartTime": "2023-09-25T12:42:04.279204638+02:00",
"Time": "2023-09-25T12:42:15.482694258+02:00",
"Value": 9
}
],
"Temporality": "CumulativeTemporality",
"IsMonotonic": true
}
}
]
},
{
"Scope": {
"Name": "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp",
"Version": "0.44.0",
"SchemaURL": ""
},
"Metrics": [
{
"Name": "http.server.request_content_length",
"Description": "",
"Unit": "",
"Data": {
"DataPoints": [
{
"Attributes": [
{
"Key": "http.flavor",
"Value": {
"Type": "STRING",
"Value": "1.1"
}
},
{
"Key": "http.method",
"Value": {
"Type": "STRING",
"Value": "GET"
}
},
{
"Key": "http.route",
"Value": {
"Type": "STRING",
"Value": "/rolldice/Alice"
}
},
{
"Key": "http.scheme",
"Value": {
"Type": "STRING",
"Value": "http"
}
},
{
"Key": "http.status_code",
"Value": {
"Type": "INT64",
"Value": 200
}
},
{
"Key": "net.host.name",
"Value": {
"Type": "STRING",
"Value": "localhost"
}
},
{
"Key": "net.host.port",
"Value": {
"Type": "INT64",
"Value": 8080
}
}
],
"StartTime": "2023-09-25T12:42:04.279212238+02:00",
"Time": "2023-09-25T12:42:15.482695758+02:00",
"Value": 0
}
],
"Temporality": "CumulativeTemporality",
"IsMonotonic": true
}
},
{
"Name": "http.server.response_content_length",
"Description": "",
"Unit": "",
"Data": {
"DataPoints": [
{
"Attributes": [
{
"Key": "http.flavor",
"Value": {
"Type": "STRING",
"Value": "1.1"
}
},
{
"Key": "http.method",
"Value": {
"Type": "STRING",
"Value": "GET"
}
},
{
"Key": "http.route",
"Value": {
"Type": "STRING",
"Value": "/rolldice/Alice"
}
},
{
"Key": "http.scheme",
"Value": {
"Type": "STRING",
"Value": "http"
}
},
{
"Key": "http.status_code",
"Value": {
"Type": "INT64",
"Value": 200
}
},
{
"Key": "net.host.name",
"Value": {
"Type": "STRING",
"Value": "localhost"
}
},
{
"Key": "net.host.port",
"Value": {
"Type": "INT64",
"Value": 8080
}
}
],
"StartTime": "2023-09-25T12:42:04.279214438+02:00",
"Time": "2023-09-25T12:42:15.482696158+02:00",
"Value": 54
}
],
"Temporality": "CumulativeTemporality",
"IsMonotonic": true
}
},
{
"Name": "http.server.duration",
"Description": "",
"Unit": "",
"Data": {
"DataPoints": [
{
"Attributes": [
{
"Key": "http.flavor",
"Value": {
"Type": "STRING",
"Value": "1.1"
}
},
{
"Key": "http.method",
"Value": {
"Type": "STRING",
"Value": "GET"
}
},
{
"Key": "http.route",
"Value": {
"Type": "STRING",
"Value": "/rolldice/Alice"
}
},
{
"Key": "http.scheme",
"Value": {
"Type": "STRING",
"Value": "http"
}
},
{
"Key": "http.status_code",
"Value": {
"Type": "INT64",
"Value": 200
}
},
{
"Key": "net.host.name",
"Value": {
"Type": "STRING",
"Value": "localhost"
}
},
{
"Key": "net.host.port",
"Value": {
"Type": "INT64",
"Value": 8080
}
}
],
"StartTime": "2023-09-25T12:42:04.279219438+02:00",
"Time": "2023-09-25T12:42:15.482697158+02:00",
"Count": 27,
"Bounds": [
0, 5, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000,
7500, 10000
],
"BucketCounts": [
0, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
],
"Min": {},
"Max": {},
"Sum": 2.1752759999999993
}
],
"Temporality": "CumulativeTemporality"
}
}
]
}
]
}
后续步骤{#next-steps}
如果想了解更多关于为你的代码插桩的内容,请参考手动插桩文档。
此外,你或许还想要配置合适的导出器(Exporter)来将你的遥测数据导出到一个或多个遥测后端。
如果你想要进一步探索更复杂的示例,可以查看
OpenTelemetry 演示,
其中包含用 Go 编写的结账服务,
产品目录服务以及会计服务。