タラバガニー設計局stalins.clubNOTE/notes/go-toolexec-integration-patterns

Goの-toolexecを実際のビルドへ差し込む方法

Goの-toolexecはビルドツール呼び出しを横取りする は強力だが、利用者が毎回

go build -toolexec=...

と書かなければならないのでは実用性が低い。実際のプロダクトは、-toolexec 自体を表に出さないためにいくつかの導入パターンを使っている。

1. 独自wrapper commandで go を包む

OpenTelemetryの otelc はこの形を採る。

otelc go build ./...

利用者から見ると「go build にprefixを付ける」だけで、内部では otelc が必要なsetupを行い、-toolexec を付けてGo commandを起動する。

DatadogのOrchestrionも同様に、専用commandを入口にできる。

この方式の利点は、

  • setup処理を先に実行できる
  • 必要な環境変数を一括設定できる
  • -toolexec のquotingを利用者へ見せずに済む
  • go build 以外の go test 等にも一貫して適用しやすい

ことにある。

compile-time instrumentationのように前処理が多いtoolでは、このwrapper方式が最も自然である。

2. GOFLAGSへ注入する

既存のMakefileやCIが go build を直接呼んでおり、command lineを書き換えにくい場合は GOFLAGS が使える。

OpenTelemetryの現在のドキュメントには、

otelc setup
export GOFLAGS="${GOFLAGS} '-toolexec=otelc toolexec'"
go build ./...

というdrop-in方式がある。

Antithesisのcoverage instrumentationも、

ENV GOFLAGS=-toolexec=antithesis-go-toolexec
RUN go build -o /app ./cmd/app
RUN go test ./...

という使い方を案内している。

この方式ではMakefile側に専用commandを強制しなくても、そのprocess environment配下のGo buildを横断的にinstrumentできる。

ただし作用範囲が広い。CI job全体に GOFLAGS を入れると、意図していない go test やtool buildまでinstrument対象になり得る。

3. tool自身がflagsを出力する

goccy/tobariは

GOFLAGS="$(tobari flags)" go test ./...

のように、必要な -cover と -toolexec をtool自身に生成させる。

これは「build commandは標準のままにしたいが、flagの組み立てはtool側に任せたい」という中間形である。

複数flagが必要なtoolでは、利用者が内部実装を覚えなくてよい。

4. -toolexec をそのまま公開する

errtraceは比較的単純で、

go build -toolexec=errtrace

と直接使える。

instrumentation前のsetupが少なく、wrapper単体で完結するtoolではこれで十分である。

Makefileを強制する必要はない

-toolexec はGo command自身のflagなので、特定のbuild systemに依存しない。

したがって「Makefile経由でしかinstrumentできない」という設計にする必要はなく、むしろ実際のtoolは、

  • wrapper command
  • GOFLAGS
  • Docker/CI environment
  • direct -toolexec

の複数入口を用意する傾向がある。

利用者のbuild pipelineを所有できるならwrapper command、既存pipelineを変更したくないなら GOFLAGS が使いやすい。

設計上の注意

GOFLAGS は便利だが暗黙的である。どのbinaryがinstrument済みかを見失いやすいため、build metadataやCI logに「instrumentation enabled」を残す方がよい。

また、instrumented buildとplain buildが同じcache entryを誤って共有しないよう、tool identityを正しく扱う必要がある。この点は -toolexecでソースを書き換えるならGoのbuild cache identityを考える に続く。

実例

  • OpenTelemetry otelc: wrapper command + GOFLAGS drop-in
  • Datadog Orchestrion: wrapper command / toolexec
  • Antithesis: direct -toolexec またはDockerの GOFLAGS
  • errtrace: direct -toolexec
  • Tobari: tobari flags で GOFLAGS 用flagを生成

関連

出典

▸ ノート一覧に戻る