关于 prometheus-go-code-review
用于审查 Go 项目中 Prometheus 指标实现的技能,重点检查指标类型、标签基数、注册生命周期和命名,并要求问题关联到具体代码证据。
主要功能
- 01核对指标类型与测量语义
- 02检查标签值是否基数受限
- 03检查指标名称和单位后缀
- 04确认指标仅注册一次
- 05检查采集器并发安全及指标端点
- 06按审查门槛提交问题
适用场景
- 01审查 Go 服务的 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、单位后缀)
- 直方图使用恰当的桶边界
- 指标只注册一次,而不是每次请求都注册
- 收集器不会因竞态条件而 panic
- /metrics 端点已暴露且可访问
硬性门槛(按顺序执行)
记录问题之前,必须按顺序完成以下检查。对于明显不适用于此差异的检查,可跳过。
-
证据范围 — 列出你正在审查且涉及 Prometheus 的文件(
prometheus/client_golang、promauto、promhttp或MustRegister)。**通过标准:**你有具体的文件路径列表(来自差异或明确的文件集);没有至少一个路径时,不得声称已审查整个仓库。 -
标签基数 — 对范围内的每个
*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 端点是否已正确暴露?
评论
支持 Markdown