emahiro/b.log

Drastically Repeat Yourself !!!!

go.opentelemetry.io で spanID と traceID を取得する

Overview

タイトルの通りですが以下の Pull Request にて go.opencensus.io から go.opentelemetry.io に乗り換えたので、乗り換えた際に従来通り span と trace を取れるようにした内容についてまとめます。

github.com

 そもそも OpenTelemetry とは?

new relic の以下のエントリを参照します。

OpenTelemetryは、サービスインストゥルメンテーション(パフォーマンスを測定する方法)のオープンソースプロジェクトであり、統一規格です。Cloud Native Computing Foundation (CNCF)がスポンサーとなり、OpenTracingとOpenCensusに代わるものです。その目的は、テレメトリーデータを収集し、バックエンドプラットフォームに送信する方法を標準化することです。

ref: https://newrelic.com/jp/blog/best-practices/what-is-opentelemetry

Opencensus に変わる計装器の統一規格であり、今後はこちらに統一されていくと考えたので、slslog で使用していた go.opencensus.io の実装を go.opentelemetry.io の実装に差し替えました。

とはいえ、slslog では opentelemetry に依存しているのは span と trace の生成のみで、これ自体は opentelemetry に依存する必要はありません。ただし、今後の規格に対してあらかじめ統一しておく方がいいだろうと考えて実装しました。

Span と Trace の取得方法

基本的には Opentelemetry の公式手順に則るのが良いと思います。(私は Go を採用してますが、他の言語のドキュメントも充実しています。

今回はデフォルトの方法ではなく、マニュアルで trace と span を埋め込む処理を実装しました。

opentelemetry.io

ハマったところ

実装方法自体は Pull Request を参照してください。実際に実装する時にハマったところとしては "go.opentelemetry.io/otel/sdk/trace" を使って trace の exporter を生成してアタッチしておかないと Tracer が取得できない、ということでした。

どういうことかというと具体的な実装箇所として span を取り出す処理として

ctx, span := otel.Tracer("github.com/slslog").Start(ctx, label)

という実装をしている箇所がありますが、あらかじめ TraceProvider をセットしておかないと otel.Tracer で TraceProvider を取得できない(実際には空であり、spanID も traceID も空になる)ということがわかりました。

これは otel.Tracer(...) の実装を見ると、 https://github.com/open-telemetry/opentelemetry-go/blob/e99a0ac0b5397c9997449131f7efa7a19442d1bf/trace.go#L26-L28 にあるようにデフォルトの TraceProvider を取得しておりこれを前もって Set しておかないといけません。

また Span を始めるときの Opencensus の実装を見てみると https://github.com/census-instrumentation/opencensus-go/blob/49838f207d61097fc0ebb8aeef306913388376ca/trace/trace.go#L212-L274 になるのですが、 startSpanInternal の内部で色んな処理(ID の生成含む)を行なっています。

つまり OpenTelemetry で実装する場合を考えても、この Opencensus の頃の実装を踏襲する必要があります。

そこで調べてみると go.opentelemetry.io の中でも sdk/trace の内部で ID 生成を実装してる箇所がありました -> https://github.com/open-telemetry/opentelemetry-go/blob/main/sdk/trace/id_generator.go

特にこの中の defaultIDGenerator という処理がこの ID 生成を担当してますが、これは最終的に sdk/trace 内の ensureValidTracerProviderConfig メソッドという中で default の ID を生成する処理の中で call されており、この Call 元は sdk/trace.NewTraceProvider になります。
ref: https://github.com/open-telemetry/opentelemetry-go/blob/d96e8d2912afcb0d205a80739798b9a56a7f0e70/sdk/trace/provider.go#L100

つまりここで sdk/trace を使って TraceProvider を生成し、これをある処理における TraceProvider を指定することで NewTraceProvider が Call されたタイミングでデフォルトで生成された ID(span と trace に指定されるもの)が入ることになります。これに気づくのにだいぶ時間がかかりました。

まとめ

Opentelemetry を使った初歩的な実装を紹介しました。

See Also

pkg.go.dev

pkg.go.dev