用 Prometheus 监控 Go 应用:埋点实战

Go 应用接入 Prometheus 的完整教程:prometheus/client_golang 暴露 /metrics、Counter/Gauge/Histogram 三种指标埋点、中间件统一采集 HTTP 指标、常见问题。

最佳实践
用 Prometheus 监控 Go 应用:埋点实战技术指南封面

Go 应用接入 Prometheus 只需一个库:prometheus/client_golang。暴露 /metrics 端点,埋点自定义指标,Prometheus 来抓取——本文走完整个流程。

第一步:暴露默认指标

go get github.com/prometheus/client_golang/prometheus/promhttp

两行代码把默认指标(Go 运行时:goroutine 数、GC 耗时、内存)挂到 HTTP 服务上:

package main

import (
    "net/http"
    "github.com/prometheus/client_golang/prometheus/promhttp"
)

func main() {
    http.Handle("/metrics", promhttp.Handler())
    http.ListenAndServe(":8080", nil)
}

访问 http://localhost:8080/metrics 就能看到 go_goroutinesgo_memstats_*process_* 等默认指标。

第二步:埋点业务指标

Counter:累计请求数

var httpRequestsTotal = prometheus.NewCounterVec(
    prometheus.CounterOpts{
        Name: "http_requests_total",
        Help: "Total number of HTTP requests",
    },
    []string{"method", "path", "status"},
)

prometheus.MustRegister(httpRequestsTotal)

// 处理请求时
httpRequestsTotal.WithLabelValues("GET", "/api/users", "200").Inc()

Gauge:当前活跃请求

var activeRequests = prometheus.NewGauge(
    prometheus.GaugeOpts{
        Name: "http_active_requests",
        Help: "Number of active HTTP requests",
    },
)

// 中间件里
activeRequests.Inc()
defer activeRequests.Dec()

Histogram:请求延迟分布

var requestDuration = prometheus.NewHistogramVec(
    prometheus.HistogramOpts{
        Name:    "http_request_duration_seconds",
        Help:    "Request duration in seconds",
        Buckets: []float64{0.01, 0.05, 0.1, 0.5, 1, 2.5, 5},
    },
    []string{"path"},
)

第三步:统一中间件

手写埋点容易漏,用中间件统一采集所有路由:

func metricsMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        activeRequests.Inc()
        defer activeRequests.Dec()

        rw := &statusWriter{ResponseWriter: w, status: 200}
        next.ServeHTTP(rw, r)

        path := routePattern(r)   // 用路由模板而非原始路径!
        requestDuration.WithLabelValues(path).Observe(time.Since(start).Seconds())
        httpRequestsTotal.WithLabelValues(r.Method, path, strconv.Itoa(rw.status)).Inc()
    })
}

⚠️ 最关键的坑:标签里用路由模板/users/{id})而不是原始路径(/users/123)。原始路径会把每个 ID 变成一条新时间序列,基数爆炸,Prometheus 会被拖垮。Gin 用 c.FullPath(),Echo 用 c.Path(),chi 用 RouteContext

第四步:Prometheus 抓取

scrape_configs:
  - job_name: go-app
    scrape_interval: 15s
    static_configs:
      - targets: ['app:8080']

查询验证:

rate(http_requests_total[5m])
histogram_quantile(0.99, sum by (le, path) (rate(http_request_duration_seconds_bucket[5m])))

观测云对照

如果不想自建 Prometheus 这套基础设施,观测云提供更短的路径:DataKit 可抓取应用的 /metrics 端点或直接接收 remote write;对 Go 应用更推荐接 APM 探针(无侵入或低开销)——请求速率、错误率、延迟分布自动采集,无需手写埋点,还能获得分布式追踪能力;已埋点的自定义指标照常上报,与 APM 数据在同一平台联动。

常见问题(FAQ)

Q:Counter 的值重启后归零,会不会丢数据?
A:Prometheus 的 rate/increase 会自动处理重置,长期趋势不受影响。这正是 Counter 设计成"只增"的原因——不需要进程持久化状态。

Q:goroutine 数突然飙升说明什么?
A:goroutine 泄漏的典型信号——某个地方起了 goroutine 没有退出(常见:channel 阻塞、http body 没关、context 没取消)。用 pprof 的 goroutine profile 定位泄漏点。

Q:默认指标里 process_ 和 go_ 有什么区别?**
A:go_* 是 Go 运行时视角(GC、goroutine、堆),process_* 是操作系统进程视角(CPU 时间、RSS、文件描述符)。排查内存问题两个都要看。

获取专属方案

联系我们

加入社区

微信扫码
加入官方交流群

立即体验

在线开通,按量计费,真正的云服务!

立即开始

选择观测云版本

代码托管平台