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 +GOFLAGSdrop-in - Datadog Orchestrion: wrapper command / toolexec
- Antithesis: direct
-toolexecまたはDockerのGOFLAGS - errtrace: direct
-toolexec - Tobari:
tobari flagsでGOFLAGS用flagを生成
関連
- Goの-toolexecはビルドツール呼び出しを横取りする
- GoのOpenTelemetryコンパイル時計装は何をしているのか
- -toolexecでソースを書き換えるならGoのbuild cache identityを考える