GoのOpenTelemetryコンパイル時計装は何をしているのか
GoではJavaのjavaagentのように、実行時に任意のバイトコードを書き換える仕組みを前提にしにくい。その代わり、OpenTelemetryのGo向けcompile-time instrumentationは、Go標準ツールチェーンの -toolexec を使ってコンパイル直前にソースへ計装コードを差し込む。
現在の中心実装は otelc である。通常の
go build ./...
を
otelc go build ./...
のように包み、内部で Goの-toolexecはビルドツール呼び出しを横取りする を使って各packageのcompileを捕捉する。
何が「zero-code」なのか
ここでいうzero-codeは「テレメトリ処理が実行時に存在しない」という意味ではない。アプリケーションのソースコードを手で修正しなくても、ビルド時に必要なhookを埋め込める、という意味である。
したがって性質は次のようになる。
- runtime agentをattachする必要はない
- third-party dependencyや標準ライブラリも、ビルド対象であれば計装できる
- 計装されたhook自体は実行時に動くので、trace生成などのruntime costは残る
- source treeそのものを書き換える必要はなく、一時的に変換した入力をcompilerへ渡せる
この違いは重要で、「runtime overheadがゼロ」というより「runtime bytecode rewritingや常駐agentが不要」と理解する方が正確である。
2025年から2026年にかけての流れ
OpenTelemetryでは2025年に、Alibabaのcompile-time instrumentation実装とDatadogのOrchestrionが寄贈候補として集約された。どちらもGoの -toolexec を利用して、compilerへ渡るGo sourceを変換するという同じ大枠を持っていた。
その後、OpenTelemetryのcompile-time instrumentation projectとして統合が進み、2026年7月のドキュメントではv1.0.0以降をproduction-readyとして扱っている。
これは、Goの自動計装が「eBPFによる外部観測だけ」ではなく、build pipelineへ介入してアプリ内部へhookを焼き込む方法も正式な選択肢になったということになる。
otelcのビルド処理
概念的には次の流れになる。
- dependency graphを調べる
- 対象package/functionに対応するinstrumentation ruleを選ぶ
-toolexecでcompile invocationを捕捉する- 対象関数へbefore/after hookやcall-site wrapping等を差し込む
- 変換済みsourceを通常のGo compilerへ渡す
- hook実装を含んだ通常のGo binaryを生成する
instrumentation ruleは対象package、関数、receiver等を指定し、どこに何を差し込むかを宣言する。現在の実装ではfunction hookだけでなく、call-site wrapping、field injection、file追加なども扱う。
なぜGoではこの方式が面白いのか
Goは静的リンクされた単一binaryを作ることが多く、実行時に外部agentから言語ランタイムへ介入する余地がJavaほど大きくない。一方で go build はcompilerやassembler等を子processとして起動するため、その入口を公式の -toolexec で差し替えられる。
つまりGoにおけるzero-code instrumentationは、runtimeの拡張点ではなくtoolchainの拡張点を利用する。
この仕組み自体はOpenTelemetry専用ではなく、Goの-toolexecを実際のビルドへ差し込む方法 にあるようにcoverage、error wrapping、test instrumentationなどにも使われている。
eBPF型との違い
OpenTelemetryにはGo向けeBPF auto instrumentationも存在する。
compile-time方式はビルド工程を変更できる環境に向き、アプリ内部の関数へ直接hookを入れられる。一方eBPF方式は既存binaryへ後付けしやすく、build pipelineを変えなくてもよい代わりに、kernel機能や権限、観測可能な境界に依存する。
両者は競合というより、介入する層が違う。
関連
- Goの-toolexecはビルドツール呼び出しを横取りする
- Goの-toolexecを実際のビルドへ差し込む方法
- -toolexecでソースを書き換えるならGoのbuild cache identityを考える