マルチモジュールプロジェクト
複数のGoモジュールで構成されたプロジェクトでwhatap-go-instを使用する方法を説明します。
用語整理
| 用語 | 説明 | 例 |
|---|---|---|
| モジュール | go.modがある単位 | module mycompany/user-api |
| パッケージ | ディレクトリレベルのコードグループ | mycompany/user-api/pkg/auth |
| 外部モジュール | go getで取得した依存関係 | github.com/gin-gonic/gin |
| ローカルモジュール | 同じプロジェクト内の別モジュール | replace ../shared-lib |
パッケージタイプ別の処理
whatap-go-instはパッケージの位置によって計測有無を決定します。
toolexecスキップルール
// 1. Go標準ライブラリ → スキップ
if strings.HasPrefix(path, os.Getenv("GOROOT")) {
return true // 変換しない
}
// 2. 外部パッケージ (go get) → スキップ
if strings.HasPrefix(path, os.Getenv("GOMODCACHE")) {
return true // 変換しない
}
// 3. その他 (自分のコード) → 変換
return false
要約
| パッケージタイプ | パス | 計測有無 |
|---|---|---|
| Go 標準ライブラリ | $GOROOT/src/... | スキップ |
| 外部モジュール (go get) | $GOMODCACHE/... | スキップ |
| 自分のプロジェクトコード | ローカルパス | 計測 |
| replaceローカルモジュール | ローカルパス (../) | 計測 (fastモードのみ) |
マルチモジュールシナリオ
シナリオ: 3つの分離されたモジュール
C:/projects/
├── db-lib/ # モジュールA: DBロジックライブラリ
│ ├── go.mod # module mycompany/db-lib
│ ├── connection.go # sql.Open()使用
│ └── query.go
│
├── web-lib/ # モジュールB: Webサーバーライブラリ
│ ├── go.mod # module mycompany/web-lib
│ ├── server.go # gin.Default()使用
│ └── handler.go
│
└── main-app/ # モジュールC: メインアプリ
├── go.mod # module mycompany/main-app
└── main.go # A、Bをimport
モジュールCのgo.mod
module mycompany/main-app
go 1.21
require (
mycompany/db-lib v1.0.0
mycompany/web-lib v1.0.0
)
推奨アプローチ
方式1: モジュール別個別inject (デプロイ用)
本番環境デプロイに推奨します。
Step 1: 各モジュールをinject
# db-libを計測
cd db-lib
whatap-go-inst inject -s . -o ../db-lib-instrumented
# web-libを計測
cd web-lib
whatap-go-inst inject -s . -o ../web-lib-instrumented
# main-appを計測
cd main-app
whatap-go-inst inject -s . -o ../main-app-instrumented
Step 2: 計測されたバージョンに置換
// main-app-instrumented/go.mod
replace mycompany/db-lib => ../db-lib-instrumented
replace mycompany/web-lib => ../web-lib-instrumented
Step 3: ビルド
cd main-app-instrumented
go build ./...
方式2: Monorepo構造 (新規プロジェクト)
新規プロジェクトなら単一モジュール構造が最もシンプルです。
myproject/
├── go.mod # 単一モジュール mycompany/myproject
├── cmd/
│ └── main/
│ └── main.go # mainパッケージ
├── internal/
│ ├── db/
│ │ └── connection.go
│ └── web/
│ └── server.go
└── pkg/
└── shared/
└── utils.go
# ビルド
whatap-go-inst go build ./cmd/main
モード別動作比較
--wrapモード
| 項目 | --wrapモード |
|---|---|
| mainモジュール | 計測される |
| replaceモジュール | 計測されない |
| 外部モジュール (GOMODCACHE) | スキップ |
| 事前条件 | なし |
replace処理
// --wrapモードはreplaceパスのみ調整
replace mycompany/db-lib => ../db-lib
// 一時ディレクトリにコピー後:
replace mycompany/db-lib => /original/path/to/db-lib // 元を参照!
結果: replace対象モジュールはコピーされず、元の(非計測)コードを参照
replaceモジュールも計測するには: 各モジュールを個別にinjectする必要があります。
# 各モジュール個別inject
whatap-go-inst inject -s ../db-lib -o ../db-lib-instrumented
whatap-go-inst inject -s . -o ./instrumented
# instrumented/go.modでreplaceパスを修正
# replace mycompany/db-lib => ../db-lib-instrumented
注意事項
1. mainがないライブラリinject
main関数がないライブラリもinjectできます。
| 項目 | 処理 |
|---|---|
trace.Init() | 追加されない (正常) |
sql.Open() → whatapsql.Open() | 変換される |
gin.Default() + ミドルウェア | 追加される |
# ライブラリモジュールinject
cd db-lib
whatap-go-inst inject -s . -o ./instrumented
# 結果: whatapsql.Open()に変換される
# trace.Init()は追加されない (mainでのみ呼び出す必要がある)
2. 依存関係の伝播
計測されたライブラリを使用するモジュールもwhatap依存関係が必要です。
// db-lib-instrumented/connection.go
import "github.com/whatap/go-api/instrumentation/.../whatapsql"
// main-appでビルドするにはこの依存関係が必要
cd main-app
go get github.com/whatap/go-api@latest
FAQ
Q1: replaceモジュールも計測されますか?
A: いいえ、wrapモードはreplaceモジュールを計測しません。replaceパスのみ調整し、元を参照します。replaceモジュールも計測するには、各モジュールを個別にinjectする必要があります。
各モジュール個別inject
whatap-go-inst inject -s ../db-lib -o ../db-lib-inst
whatap-go-inst inject -s ../web-lib -o ../web-lib-inst
Q2: 外部ライブラリ(gin、gormなど)を計測できますか?
A: いいえ。外部ライブラリは$GOMODCACHEにあるためスキップされます。これは意図された動作です。
- 外部ライブラリ修正時、他のプロジェクトに影響
- Goモジュールシステムの不変性原則
代わりに、ユーザーコードでの外部ライブラリ呼び出しが変換されます:
sql.Open()→whatapsql.Open()gin.Default()→ ミドルウェア自動追加
Q3: Monorepo vs マルチモジュール、どちらが良いですか?
| 構造 | 長所 | 短所 |
|---|---|---|
| Monorepo | 計測が簡単、依存関係管理が容易 | モジュール再利用が困難 |
| マルチモジュール | 独立したモジュールデプロイが可能 | 計測設定が複雑 |
推奨:
- 新規プロジェクト → Monorepo
- 既存マルチモジュール → モジュール別個別inject
Q4: CI/CDでマルチモジュールをどのようにビルドしますか?
下記のCI/CD例セクションを参照してください。
CI/CD例
GitHub Actions
# .github/workflows/build.yml
name: Build with WhaTap Instrumentation
jobs:
build:
runs-on: ubuntu-latest
steps:
# 1. すべてのモジュールをチェックアウト
- uses: actions/checkout@v3
with:
path: main-app
- uses: actions/checkout@v3
with:
repository: mycompany/db-lib
path: db-lib
- uses: actions/checkout@v3
with:
repository: mycompany/web-lib
path: web-lib
# 2. Goインストール
- uses: actions/setup-go@v4
with:
go-version: '1.21'
# 3. whatap-go-instインストール
- run: go install github.com/whatap/go-api-inst/cmd/whatap-go-inst@latest
# 4. ビルド
- run: |
cd main-app
whatap-go-inst go build -o myapp ./...
# 5. アーティファクトアップロード
- uses: actions/upload-artifact@v3
with:
name: myapp
path: main-app/myapp
GitLab CI
# .gitlab-ci.yml
stages:
- build
build:
stage: build
image: golang:1.21
before_script:
# whatap-go-instインストール
- go install github.com/whatap/go-api-inst/cmd/whatap-go-inst@latest
# 他のモジュールをクローン
- git clone https://gitlab.com/mycompany/db-lib.git ../db-lib
- git clone https://gitlab.com/mycompany/web-lib.git ../web-lib
script:
- whatap-go-inst go build -o myapp ./...
artifacts:
paths:
- myapp
Dockerマルチステージビルド
# Dockerfile
FROM golang:1.21 AS builder
WORKDIR /workspace
# whatap-go-instインストール
RUN go install github.com/whatap/go-api-inst/cmd/whatap-go-inst@latest
# ソースコピー (すべてのモジュール)
COPY db-lib/ ./db-lib/
COPY web-lib/ ./web-lib/
COPY main-app/ ./main-app/
# 初期化および依存関係を追加
WORKDIR /workspace/main-app
# ビルド
RUN whatap-go-inst go build -o /app/myapp ./...
# ランタイムイメージ
FROM alpine:latest
COPY /app/myapp /app/myapp
CMD ["/app/myapp"]