mcpField
P
prometheus-go-code-review

prometheus-go-code-review

github.com/existential-birds测试与质量85121 次安装11 周前更新
代码仓库

关于 prometheus-go-code-review

用于审查 Go 项目中 Prometheus 指标实现的技能,重点检查指标类型、标签基数、注册生命周期和命名,并要求问题关联到具体代码证据。

主要功能

  1. 01核对指标类型与测量语义
  2. 02检查标签值是否基数受限
  3. 03检查指标名称和单位后缀
  4. 04确认指标仅注册一次
  5. 05检查采集器并发安全及指标端点
  6. 06按审查门槛提交问题

适用场景

  1. 01审查 Go 服务的 Prometheus 指标定义
  2. 02排查高基数标签和指标类型错误
  3. 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 端点已暴露且可访问

硬性门槛(按顺序执行)

记录问题之前,必须按顺序完成以下检查。对于明显不适用于此差异的检查,可跳过。

  1. 证据范围 — 列出你正在审查且涉及 Prometheus 的文件(prometheus/client_golang、promauto、promhttp 或 MustRegister)。**通过标准:**你有具体的文件路径列表(来自差异或明确的文件集);没有至少一个路径时,不得声称已审查整个仓库。

  2. 标签基数 — 对范围内的每个 *Vec 或带标签的指标,列出标签名称及其值的来源(常量、有界代码,还是请求派生的字符串)。**通过标准:**没有标签使用无界值(例如原始 user_id、完整 URL 路径、时间戳),除非代码使用了有界映射并且你能指出该映射。

  3. 注册生命周期 — 对范围内的指标定义,确认构造函数只运行一次(包级 var、init 或 sync.Once),而不是在每请求处理程序中运行。**通过标准:**不存在针对同一个逻辑指标、在每次请求时都分配或注册新的 Counter/Histogram/*Vec 的模式。

  4. 问题描述格式 — 每个问题都要指出文件(尽可能注明行号或符号),说明如果问题属实会违反哪项门槛(2 或 3),并关联到观察到的代码。**通过标准:**对于已满足门槛 2–3 的代码,不得单独提出风格方面的小问题。

指标类型选择

测量内容类型示例
已处理的请求Counterrequests_total
队列中的项目Gaugequeue_length
请求时长Histogramrequest_duration_seconds
并发连接数Gaugeactive_connections
启动以来的错误数Countererrors_total
内存使用量Gaugememory_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)
}

审查问题

  1. 指标类型是否正确(Counter、Gauge 还是 Histogram)?
  2. 标签值是否有界(不包含 UUID、时间戳、路径)?
  3. 指标名称是否包含单位(_seconds、_bytes)?
  4. 指标是否只注册一次(而非每次请求都注册)?
  5. /metrics 端点是否已正确暴露?

评论

支持 Markdown