prometheus-go-code-review について
Go プロジェクトの Prometheus 計測コードをレビューするスキルです。メトリクス型、ラベルのカーディナリティ、登録ライフサイクル、命名を確認し、指摘には具体的なコード上の根拠を求めます。
主な機能
- 01測定内容に合うメトリクス型の確認
- 02ラベル値が有界かの点検
- 03メトリクス名と単位サフィックスの確認
- 04メトリクスが一度だけ登録されるか確認
- 05コレクターの並行処理と指標エンドポイントの確認
- 06段階的なレビューゲートに基づく指摘
活用シーン
- 01Go サービスの Prometheus メトリクスをレビュー
- 02高カーディナリティのラベルや型の誤りを発見
- 03登録箇所と /metrics の公開を検証
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ existential-birds/ beagle --skill prometheus-go-code-reviewインストール後、Claude Code で/prometheus-go-code-reviewを入力して読み込まれたか確認します。
詳細
Prometheus Go コードレビュー
レビューチェックリスト
- メトリクスの種類が測定の意味と一致している(Counter/Gauge/Histogram)
- ラベルのカーディナリティが低い(ユーザー ID、タイムスタンプ、パスを含まない)
- メトリクス名が規則に従っている(snake_case、単位のサフィックス)
- ヒストグラムで適切なバケット境界を使用している
- メトリクスは一度だけ登録され、リクエストごとに登録されていない
- コレクターが競合状態でパニックを起こさない
-
/metricsエンドポイントが公開され、アクセス可能である
ハードゲート(順番に実施)
指摘事項を記録する前に、順番に完了してください。差分に明らかに該当しないゲートはスキップしてください。
-
エビデンスの範囲 — Prometheus(
prometheus/client_golang、promauto、promhttp、またはMustRegister)に関係するレビュー対象ファイルを列挙します。合格条件:(差分または明示的なファイルセットから)具体的なパスの一覧があること。少なくとも 1 つのパスを示さずにリポジトリ全体について主張しないこと。 -
ラベルのカーディナリティ — 対象範囲内の各
*Vecまたはラベル付きメトリクスについて、ラベル名と値の取得元(定数、上限のあるコード、リクエスト由来の文字列など)を列挙します。合格条件: コードで上限のあるマッピングを使用し、その箇所を明示している場合を除き、上限のない値(生のuser_id、完全な URL パス、タイムスタンプなど)を使うラベルがないこと。 -
登録のライフサイクル — 対象範囲内のメトリクス定義について、コンストラクターが(パッケージレベルの
var、init、またはsync.Onceによって)一度だけ実行され、リクエストごとのハンドラー内では実行されないことを確認します。合格条件: 同一の論理メトリクスに対して、リクエストごとに新しいCounter/Histogram/*Vecを割り当て、登録するパターンがないこと。 -
指摘事項の形式 — 各指摘事項でファイル名(可能であれば行番号またはシンボル名)を示し、問題が実際にある場合にどのゲート(2 または 3)に抵触するかを述べ、確認したコードに結び付けます。合格条件: 該当コードがゲート 2~3 を満たしている場合、単独のスタイル上の指摘をしないこと。
メトリクスの種類の選択
| 測定対象 | 種類 | 例 |
|---|---|---|
| 処理したリクエスト数 | Counter | requests_total |
| キュー内のアイテム数 | Gauge | queue_length |
| リクエストの所要時間 | Histogram | request_duration_seconds |
| 同時接続数 | Gauge | active_connections |
| 起動後のエラー数 | Counter | errors_total |
| メモリ使用量 | Gauge | memory_bytes |
重大なアンチパターン
1. カーディナリティの高いラベル
// BAD - unique per user/request
counter := promauto.NewCounterVec(
prometheus.CounterOpts{Name: "requests_total"},
[]string{"user_id", "path"}, // millions of series!
)
counter.WithLabelValues(userID, request.URL.Path).Inc()
// GOOD - bounded label values
counter := promauto.NewCounterVec(
prometheus.CounterOpts{Name: "requests_total"},
[]string{"method", "status_code"}, // <100 series
)
counter.WithLabelValues(r.Method, statusCode).Inc()
2. 誤ったメトリクスの種類
// BAD - using gauge for monotonic value
requestCount := promauto.NewGauge(prometheus.GaugeOpts{
Name: "http_requests",
})
requestCount.Inc() // should be Counter!
// GOOD
requestCount := promauto.NewCounter(prometheus.CounterOpts{
Name: "http_requests_total",
})
requestCount.Inc()
3. リクエストごとの登録
// BAD - new metric per request
func handler(w http.ResponseWriter, r *http.Request) {
counter := prometheus.NewCounter(...) // creates new each time!
prometheus.MustRegister(counter) // panics on duplicate!
}
// GOOD - register once
var requestCounter = promauto.NewCounter(prometheus.CounterOpts{
Name: "http_requests_total",
})
func handler(w http.ResponseWriter, r *http.Request) {
requestCounter.Inc()
}
4. 単位のサフィックスがない
// BAD
duration := promauto.NewHistogram(prometheus.HistogramOpts{
Name: "request_duration", // no unit!
})
// GOOD
duration := promauto.NewHistogram(prometheus.HistogramOpts{
Name: "request_duration_seconds", // unit in name
})
優れたパターン
メトリクスの定義
var (
httpRequests = promauto.NewCounterVec(
prometheus.CounterOpts{
Namespace: "myapp",
Subsystem: "http",
Name: "requests_total",
Help: "Total HTTP requests processed",
},
[]string{"method", "status"},
)
httpDuration = promauto.NewHistogramVec(
prometheus.HistogramOpts{
Namespace: "myapp",
Subsystem: "http",
Name: "request_duration_seconds",
Help: "HTTP request latencies",
Buckets: []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10},
},
[]string{"method"},
)
)
ミドルウェアのパターン
func metricsMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
timer := prometheus.NewTimer(httpDuration.WithLabelValues(r.Method))
defer timer.ObserveDuration()
wrapped := &responseWriter{ResponseWriter: w, status: 200}
next.ServeHTTP(wrapped, r)
httpRequests.WithLabelValues(r.Method, strconv.Itoa(wrapped.status)).Inc()
})
}
メトリクスの公開
import "github.com/prometheus/client_golang/prometheus/promhttp"
func main() {
http.Handle("/metrics", promhttp.Handler())
http.ListenAndServe(":9090", nil)
}
レビューの質問
- メトリクスの種類は適切ですか(Counter、Gauge、Histogram のどれですか)?
- ラベルの値には上限がありますか(UUID、タイムスタンプ、パスを含みませんか)?
- メトリクス名に単位(
_seconds、_bytes)が含まれていますか? - メトリクスは一度だけ登録されていますか(リクエストごとに登録されていませんか)?
/metricsエンドポイントは適切に公開されていますか?