OpenTelemetry Go SDK¶
OpenTelemetry Go SDK は、API、SDK、およびフレームワークのインスツルメンテーションライブラリを通じて、Go アプリケーションのテレメトリデータを収集します。Java Agent のようなランタイム自動インスツルメンテーションとは異なり、Go アプリケーションでは通常、コード内で SDK を初期化し、対応するインスツルメンテーションライブラリを使用して Web フレームワーク、HTTP クライアント、データベース、またはメッセージキューをラップする必要があります。
このドキュメントでは、DataKit の OpenTelemetry コレクターを使用して OTLP データを受信し、Guance に転送します。
このドキュメントのサンプルでは、OTLP/HTTP + Protobuf を使用して Trace と Metric を報告します。OpenTelemetry Go の Trace と Metric は安定した状態にあります。Log SDK の成熟度とエコシステムサポートは依然として変更される可能性があるため、本番環境に接続する前に、現在の公式ステータスを確認する必要があります。
前提条件¶
- Go 1.23 以降。
- DataKit がインストールされており、対象の Guance ワークスペースに接続されていること。
- Go アプリケーションから DataKit へのネットワーク到達性があること。OTLP/HTTP は DataKit HTTP ポート
9529を使用し、OTLP/gRPC はデフォルトで4317を使用します。 - アプリケーションで使用するフレームワークまたはコンポーネントに対応する OpenTelemetry Go インスツルメンテーションライブラリ が存在すること、または OpenTelemetry API を使用して手動で Span と Metric を作成する予定があることを確認してください。
一、OpenTelemetry コレクターを有効にする¶
DataKit インストールディレクトリの conf.d/opentelemetry に移動します。コレクター設定がまだ作成されていない場合は、サンプルファイルをコピーします。
opentelemetry.conf に、少なくとも以下の受信設定が含まれていることを確認します。
[[inputs.opentelemetry]]
# カスタム属性を Guance でタグとして保持するには、ここにホワイトリストを追加します。
# 属性名のドットはアンダースコアに変換されます(例:team.name -> team_name)。
customer_tags = ["team", "project"]
[inputs.opentelemetry.http]
http_status_ok = 200
trace_api = "/otel/v1/traces"
metric_api = "/otel/v1/metrics"
logs_api = "/otel/v1/logs"
[inputs.opentelemetry.grpc]
addr = "127.0.0.1:4317"
max_payload = 16777216
上記の設定により、以下の受信アドレスが有効になります。
| プロトコル | データタイプ | DataKit 受信アドレス |
|---|---|---|
| OTLP/HTTP + Protobuf | Trace | http://<DataKit-IP>:9529/otel/v1/traces |
| OTLP/HTTP + Protobuf | Metric | http://<DataKit-IP>:9529/otel/v1/metrics |
| OTLP/HTTP + Protobuf | Log | http://<DataKit-IP>:9529/otel/v1/logs |
| OTLP/gRPC | Trace、Metric、Log | http://<DataKit-IP>:4317 |
アプリケーションと DataKit が同じホスト上にない場合は、実際のデプロイに応じて DataKit HTTP リスニングアドレス、ファイアウォール、またはその他のネットワークアクセス制御を調整する必要があります。OTLP/gRPC を使用する場合は、addr をアプリケーションがアクセス可能なリスニングアドレス(例:0.0.0.0:4317)に変更する必要もあります。OTLP 受信ポートをパブリックネットワークに直接公開しないでください。
DataKit を再起動して設定を有効にします。
DataKit HTTP サービスが到達可能かどうかを確認します。
二、アプリケーションへの OpenTelemetry 導入¶
Go SDK とインスツルメンテーションライブラリのインストール¶
Go プロジェクトディレクトリで、公式 SDK、OTLP/HTTP Exporter、および net/http インスツルメンテーションライブラリをインストールします。
go get go.opentelemetry.io/otel
go get go.opentelemetry.io/otel/sdk
go get go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp
go get go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp
go get go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp
go mod tidy
go.mod と go.sum は依存関係のバージョンを記録します。本番環境ではこれらのファイルをコミットし、OpenTelemetry の依存関係をアップグレードした後は、コンパイル、単体テスト、およびトレーシングのリグレッションテストを実行する必要があります。
SDK の初期化¶
以下のサンプルは、次の処理を同時に実行します。
OTEL_SERVICE_NAMEとOTEL_RESOURCE_ATTRIBUTESからリソース属性を読み取ります。- OTLP/HTTP Trace Exporter と Metric Exporter を作成します。
- グローバルな
TracerProvider、MeterProvider、および W3C コンテキストプロパゲーターを登録します。 otelhttpを使用して HTTP Handler をラップし、ビジネスサブ Span とカスタム Counter を作成します。- プロセス終了時に Provider をフラッシュしてシャットダウンし、バッファ内のデータ損失を防ぎます。
package main
import (
"context"
"errors"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
"go.opentelemetry.io/otel/propagation"
sdkmetric "go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
)
func setupOTelSDK(ctx context.Context) (func(context.Context) error, error) {
res, err := resource.New(
ctx,
resource.WithFromEnv(),
resource.WithTelemetrySDK(),
resource.WithHost(),
resource.WithOS(),
resource.WithProcess(),
)
if err != nil {
return nil, fmt.Errorf("create resource: %w", err)
}
traceExporter, err := otlptracehttp.New(ctx)
if err != nil {
return nil, fmt.Errorf("create trace exporter: %w", err)
}
tracerProvider := sdktrace.NewTracerProvider(
sdktrace.WithResource(res),
sdktrace.WithSampler(
sdktrace.ParentBased(sdktrace.TraceIDRatioBased(1.0)),
),
sdktrace.WithBatcher(traceExporter),
)
metricExporter, err := otlpmetrichttp.New(ctx)
if err != nil {
_ = tracerProvider.Shutdown(ctx)
return nil, fmt.Errorf("create metric exporter: %w", err)
}
meterProvider := sdkmetric.NewMeterProvider(
sdkmetric.WithResource(res),
sdkmetric.WithReader(
sdkmetric.NewPeriodicReader(
metricExporter,
sdkmetric.WithInterval(30*time.Second),
),
),
)
otel.SetTracerProvider(tracerProvider)
otel.SetMeterProvider(meterProvider)
otel.SetTextMapPropagator(
propagation.NewCompositeTextMapPropagator(
propagation.TraceContext{},
propagation.Baggage{},
),
)
shutdown := func(ctx context.Context) error {
return errors.Join(
meterProvider.Shutdown(ctx),
tracerProvider.Shutdown(ctx),
)
}
return shutdown, nil
}
func main() {
ctx, stop := signal.NotifyContext(
context.Background(),
os.Interrupt,
syscall.SIGTERM,
)
defer stop()
shutdown, err := setupOTelSDK(ctx)
if err != nil {
log.Fatal(err)
}
defer func() {
shutdownCtx, cancel := context.WithTimeout(
context.Background(),
5*time.Second,
)
defer cancel()
if err := shutdown(shutdownCtx); err != nil {
log.Printf("shutdown OpenTelemetry: %v", err)
}
}()
meter := otel.Meter("example/order-service")
requestCounter, err := meter.Int64Counter("app.request.count")
if err != nil {
log.Fatal(err)
}
mux := http.NewServeMux()
mux.Handle("/hello", otelhttp.NewHandler(
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
requestCtx, span := otel.Tracer("example/order-service").Start(
r.Context(),
"prepare-response",
)
defer span.End()
span.SetAttributes(attribute.String("app.route", "/hello"))
requestCounter.Add(requestCtx, 1)
_, _ = fmt.Fprintln(w, "hello from OpenTelemetry Go SDK")
}),
"GET /hello",
))
server := &http.Server{
Addr: ":8080",
Handler: mux,
ReadHeaderTimeout: 5 * time.Second,
}
go func() {
<-ctx.Done()
shutdownCtx, cancel := context.WithTimeout(
context.Background(),
5*time.Second,
)
defer cancel()
if err := server.Shutdown(shutdownCtx); err != nil {
log.Printf("shutdown HTTP server: %v", err)
}
}()
log.Println("listening on http://127.0.0.1:8080")
if err := server.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
log.Fatal(err)
}
}
実際のプロジェクトでは、使用するコンポーネントに対応するインスツルメンテーションライブラリをインストールする必要があります。例えば、標準ライブラリ net/http には otelhttp を使用します。その他の Web フレームワーク、データベース、またはメッセージキューについては、OpenTelemetry Registry から一致するパッケージを選択し、そのパッケージの説明に従って Handler、Transport、Client、または Driver をラップしてください。
報告先アドレスの設定と起動¶
以下では、OTLP/HTTP + Protobuf を使用してローカルの DataKit に報告します。OTEL_EXPORTER_OTLP_ENDPOINT はベースアドレスで、Trace と Metric Exporter はそれぞれ /v1/traces と /v1/metrics を追加し、最終的に DataKit の /otel/v1/* ルートに対応します。
export OTEL_SERVICE_NAME="order-service"
export OTEL_RESOURCE_ATTRIBUTES="deployment.environment.name=prod,service.version=1.0.0,team=backend"
export OTEL_EXPORTER_OTLP_ENDPOINT="http://127.0.0.1:9529/otel"
export OTEL_EXPORTER_OTLP_INSECURE="true"
export OTEL_EXPORTER_OTLP_COMPRESSION="gzip"
go run .
リクエストを送信して Trace と Metric を生成します。
Metric はデフォルトで 30 秒ごとにエクスポートされます。このサンプルでは sdkmetric.WithInterval(30*time.Second) で制御されています。1 エクスポート周期待機した後、Guance で service=order-service を指定してトレーシングを確認し、app.request.count メトリクスをクエリできます。
OTLP/gRPC の使用¶
Go SDK の OTLP 転送は、コードで使用される Exporter パッケージによって決まります。このサンプルでは otlptracehttp と otlpmetrichttp を直接使用しているため、OTEL_EXPORTER_OTLP_PROTOCOL=grpc を設定しても gRPC に切り替わりません。
OTLP/gRPC を使用するには、gRPC Exporter をインストールします。
go get go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc
go get go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc
go mod tidy
次に、コード内の Exporter パッケージと初期化関数をそれぞれ以下に置き換えます。
そして、/v1/traces、/v1/metrics パスを含まない gRPC アドレスを使用します。
export OTEL_EXPORTER_OTLP_ENDPOINT="http://127.0.0.1:4317"
export OTEL_EXPORTER_OTLP_INSECURE="true"
三、データ報告パラメータ¶
リソースパラメータ¶
このサンプルでは、resource.WithFromEnv() を使用して以下の標準環境変数を読み取ります。
| 環境変数 | 説明 | 推奨値または例 |
|---|---|---|
OTEL_SERVICE_NAME |
サービス名。リソース属性 service.name に対応します。 |
order-service。本番環境では明示的に設定する必要があります。 |
OTEL_RESOURCE_ATTRIBUTES |
リソース属性。カンマ区切りの key=value 形式です。 |
deployment.environment.name=prod,service.version=1.0.0,team=backend |
少なくとも service.name、deployment.environment.name、および service.version を設定することを推奨します。これらは、Guance でのサービス归属、環境フィルタリング、バージョン分析に使用されます。カスタムリソース属性をタグとして保持するには、DataKit の customer_tags ホワイトリストに追加する必要があります。属性名の . は _ に変換されます。
OTLP/HTTP パラメータ¶
otlptracehttp.New() と otlpmetrichttp.New() は、以下の環境変数を直接読み取ります。シグナル固有のパラメータは汎用パラメータよりも優先されます。
| 汎用環境変数 | シグナル固有環境変数 | 説明 | DataKit の例 |
|---|---|---|---|
OTEL_EXPORTER_OTLP_ENDPOINT |
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT、OTEL_EXPORTER_OTLP_METRICS_ENDPOINT |
汎用パラメータはベース URL で、Exporter が自動的にシグナルパスを追加します。シグナル固有パラメータは完全な URL で、そのまま使用されます。 | 汎用:http://127.0.0.1:9529/otel;Trace:http://127.0.0.1:9529/otel/v1/traces;Metric:http://127.0.0.1:9529/otel/v1/metrics |
OTEL_EXPORTER_OTLP_INSECURE |
OTEL_EXPORTER_OTLP_TRACES_INSECURE、OTEL_EXPORTER_OTLP_METRICS_INSECURE |
トランスポート層の TLS を無効にするかどうか。 | DataKit が平文の HTTP を使用する場合は true に設定します。 |
OTEL_EXPORTER_OTLP_HEADERS |
OTEL_EXPORTER_OTLP_TRACES_HEADERS、OTEL_EXPORTER_OTLP_METRICS_HEADERS |
リクエストヘッダー。カンマ区切りの key=value 形式です。 |
DataKit で expected_headers を設定する場合、対応する値を設定します。 |
OTEL_EXPORTER_OTLP_TIMEOUT |
OTEL_EXPORTER_OTLP_TRACES_TIMEOUT、OTEL_EXPORTER_OTLP_METRICS_TIMEOUT |
単一エクスポートのタイムアウト時間。値はミリ秒単位です。 | ネットワーク状況に応じて設定します(例:10000)。 |
OTEL_EXPORTER_OTLP_COMPRESSION |
OTEL_EXPORTER_OTLP_TRACES_COMPRESSION、OTEL_EXPORTER_OTLP_METRICS_COMPRESSION |
OTLP リクエストの圧縮方式。 | gzip に設定可能。圧縮しない場合は空欄にします。 |
OTEL_EXPORTER_OTLP_CERTIFICATE |
OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE、OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE |
サーバー証明書を検証するための PEM CA ファイルのパス。 | HTTPS 経由で OTLP を受信する場合、証明書のデプロイに応じて設定します。 |
アプリケーションと DataKit が同じホスト上にない場合は、サンプルの 127.0.0.1 をアプリケーションがアクセス可能な DataKit アドレスに置き換えてください。
SDK コードパラメータ¶
以下のパラメータは Go SDK の初期化コードによって制御され、同名の汎用環境変数を設定しても自動的には有効になりません。
| 設定項目 | サンプルコード | 説明 |
|---|---|---|
| Trace サンプリング | sdktrace.ParentBased(sdktrace.TraceIDRatioBased(1.0)) |
1.0 はルート Trace の全件サンプリングを示します。本番環境では容量に応じて 0.1 などの比率に調整し、ParentBased を使用して上流のサンプリング決定に従うことができます。 |
| Span の一括エクスポート | sdktrace.WithBatcher(traceExporter) |
本番環境では一括エクスポートを推奨します。WithMaxQueueSize、WithMaxExportBatchSize、WithBatchTimeout、WithExportTimeout を使用してさらに調整できます。 |
| Metric エクスポート周期 | sdkmetric.WithInterval(30*time.Second) |
定期的なエクスポート間隔を制御します。短すぎると、アプリケーション、ネットワーク、およびストレージのオーバーヘッドが増加します。 |
| コンテキスト伝搬 | TraceContext{}、Baggage{} |
W3C の traceparent、tracestate、および baggage を使用します。呼び出しチェーン上のサービスは、伝搬形式の互換性を維持する必要があります。 |
| リソース検出 | resource.WithHost()、WithOS()、WithProcess() |
ホスト、オペレーティングシステム、およびプロセス属性を自動的に追加します。プロセスパラメータやリソース属性にシークレットなどの機密情報を保存しないでください。 |
このサンプルでは OTLP Exporter を直接作成するため、特に以下の点に注意する必要があります。
OTEL_EXPORTER_OTLP_PROTOCOLは、コードによって既に選択されている HTTP または gRPC Exporter を変更しません。OTEL_TRACES_EXPORTER、OTEL_METRICS_EXPORTERは、このサンプルで直接作成された Exporter を無効にしません。- OpenTelemetry Go コア SDK は、現在、すべての汎用 SDK 環境変数を自動的に適用するわけではありません。特に、
OTEL_SDK_DISABLED、OTEL_TRACES_SAMPLER、またはOTEL_PROPAGATORSがカスタム初期化コードで自動的に有効になることを想定しないでください。 - 標準環境変数を使用して Exporter を動的に選択および初期化する必要がある場合は、公式 Contrib の
autoexportパッケージを評価し、テスト環境でその動作を検証してください。
検証とトラブルシューティング¶
curl http://127.0.0.1:9529/v1/pingを実行し、アプリケーションが DataKit にアクセスできることを確認します。- アプリケーションを起動し、
/helloにアクセスして、アプリケーションログにcreate trace exporter、create metric exporter、または OTLP エクスポートエラーが含まれていないことを確認します。 - 少なくとも 1 つの Metric エクスポート周期を待ちます。
- Guance の APM サービスリストで
order-serviceを指定して Trace をクエリします。 - データがクエリできない場合は、DataKit
opentelemetryコレクターの設定、アプリケーションから DataKit へのネットワーク、報告 URL、および DataKit ログを確認します。 - 重複する Span が発生する場合は、同じ Handler、Transport、データベース Client、または Driver が重複してラップされていないか確認してください。