Go服务暴露/metrics接口需手动定义Histogram指标并注册埋点,否则仅显示运行时数据;用NewHistogram而非NewSummary以支持分位数查询,必须设全Subsystem、Name、Buckets及method/status_code标签,并通过中间件统一Observe耗时。

Go 服务暴露的 /metrics 接口本身不包含 API 性能指标——必须手动定义、注册并埋点,否则 Prometheus 只能看到 Go 运行时数据,看不到请求耗时、错误率、QPS 等关键指标。
怎么定义 HTTP 请求耗时指标(Histogram)
用 prometheus.NewHistogram() 而不是 prometheus.NewSummary():前者支持按分位数查询(如 http_request_duration_seconds{quantile="0.95"}),后者在服务重启后分位数统计会重置,不适合长期监控。
关键参数必须设全:
-
Buckets建议用prometheus.DefBuckets(默认 0.001~10 秒共 12 档),避免自定义过窄导致大部分请求落在+Inf桶里 -
Subsystem和Name合起来构成完整指标名,比如http_request_duration_seconds,别漏掉_seconds后缀(Prometheus 强制要求单位明确) - 必须加
Labels,至少包括method、status_code,否则无法下钻分析失败接口或慢请求来源
示例:
reqDur := prometheus.NewHistogram(prometheus.HistogramOpts{
Subsystem: "http",
Name: "request_duration_seconds",
Help: "HTTP request latency in seconds",
Buckets: prometheus.DefBuckets,
Labels: []string{"method", "status_code"},
})
reg.MustRegister(reqDur)
如何在 HTTP handler 中自动埋点
不能靠每个 handler 手动调用 Observe(),容易漏、难维护。推荐用中间件封装,统一记录起止时间、状态码、方法。
注意两个坑:
- 中间件必须在
http.ServeMux或路由框架(如 Gin、Chi)的最外层注册,否则可能被子路由绕过 - 调用
Observe()前要确保 response 已写入状态码(比如w.WriteHeader(404)),否则拿到的status_code是 0 - 不要在 defer 里直接调用
Observe(),因为time.Since()的起点得是 handler 开始执行时刻,不是中间件入口时刻
简版中间件示意:
func metricsMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
// 包装 ResponseWriter 获取真实 status_code
rw := &responseWriter{ResponseWriter: w, statusCode: 200}
next.ServeHTTP(rw, r)
reqDur.WithLabelValues(r.Method, strconv.Itoa(rw.statusCode)).Observe(time.Since(start).Seconds())
})
}
为什么 Prometheus 查不到 http_request_duration_seconds
常见现象:curl /metrics 能看到指标定义,但 Prometheus Targets 显示 UP 却查不到该指标的任何 series。核心原因只有三个:
-
reqDur实例没调用reg.MustRegister()(或prometheus.MustRegister()),仅声明不注册 = 不存在 - 注册用的是
prometheus.DefaultRegisterer,但promhttp.HandlerFor()传入的是独立Registry,两者不匹配 - 指标名或 label 名含非法字符(如空格、大写字母、下划线以外的符号),Prometheus 解析失败,整个 metrics 块被跳过
验证方式:curl /metrics 返回内容中搜索 http_request_duration_seconds,如果没出现 # HELP 和 # TYPE 行,说明注册失败;如果出现了但无具体样本行(即没有 http_request_duration_seconds{...} 0.0123 这类),说明埋点逻辑没触发。
要不要用 promauto?
单包小项目可直接用 promauto.With(reg) 简化注册,但要注意:promauto.NewHistogram() 内部仍依赖全局注册器行为,若多个包都初始化同名指标(比如都叫 http_request_duration_seconds),启动 panic 报 duplicate metrics collector registration attempted。
中大型项目建议放弃 promauto,全部改用显式构造 + reg.MustRegister(),把指标定义和注册收归 main 包或统一监控模块管理。这样既能避免命名冲突,也方便后期加 label 维度(比如按 path 分组)、做指标生命周期控制(如热加载配置动态开关某类埋点)。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











