タラバガニー設計局stalins.clubNOTE/notes/go-compile-time-opentelemetry-instrumentation

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のビルド処理

概念的には次の流れになる。

  1. dependency graphを調べる
  2. 対象package/functionに対応するinstrumentation ruleを選ぶ
  3. -toolexec でcompile invocationを捕捉する
  4. 対象関数へbefore/after hookやcall-site wrapping等を差し込む
  5. 変換済みsourceを通常のGo compilerへ渡す
  6. 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機能や権限、観測可能な境界に依存する。

両者は競合というより、介入する層が違う。

関連

出典

▸ ノート一覧に戻る