暴露/metrics端点需显式注册自定义指标:默认仅含Go运行时指标,业务指标须用prometheus.MustRegister();多实例场景应使用独立Registry并传入promhttp.HandlerFor()。

怎么暴露/metrics端点?别只写promhttp.Handler()就完事
Go服务要被Prometheus拉取指标,核心就是提供一个符合规范的/metrics HTTP接口。但很多人直接抄示例,用promhttp.Handler()裸奔,结果发现:进程指标(如内存、GC)有,业务指标没注册上,或者重启后指标丢失——因为promhttp.Handler()只暴露已注册到默认注册器(prometheus.DefaultRegisterer)里的指标,而自定义指标必须显式注册。
- 默认指标(Go运行时、进程、构建信息)自动注册,开箱即用
- 自定义
Counter、Gauge等必须调用prometheus.MustRegister()或reg.MustRegister() - 多实例/热重载场景下,避免复用
DefaultRegisterer,建议用独立prometheus.NewRegistry(),再传给promhttp.HandlerFor(reg, promhttp.HandlerOpts{}) - 若用
yoyogo框架,Endpoints.UsePrometheus(router)已帮你做了注册和路由,不用手动挂/metrics
用promauto还是prometheus原生API?看是否需要跨包共享
promauto.NewCounter()看似方便,但它内部依赖全局注册器,一旦多个包都用它初始化同名指标(比如都叫http_requests_total),启动时会panic报duplicate metrics collector registration attempted。这不是bug,是Prometheus的强一致性保护。
- 单包小项目:用
promauto.With(prometheus.DefaultRegisterer)够用,代码简洁 - 中大型项目(含插件、中间件、子模块):统一用
prometheus.NewRegistry()+prometheus.NewCounter(prometheus.CounterOpts{...})+reg.MustRegister(),把注册控制权收归主应用 -
promauto不支持动态命名(如按URL路径生成不同指标),此时必须手写prometheus.NewCounterVec()并管理WithLabelValues()
为什么Prometheus查不到指标?检查这三处硬性条件
常见现象:服务跑了,curl http://localhost:8080/metrics能返回数据,但Prometheus Targets页面显示DOWN,或者Target状态是UP却查不到任何series。根本原因不是网络不通,而是违反了Prometheus拉取协议的刚性要求。
- 目标地址必须可被Prometheus容器/进程网络访问:Docker部署时,
localhost在容器内指自身,不是宿主机;应改用宿主机IP或host.docker.internal(Mac/Win)或network_mode: host - 抓取路径必须严格匹配配置中的
metrics_path(默认/metrics),且响应Content-Type必须是text/plain; version=0.0.4(promhttp默认满足) - 指标名称必须符合命名规范:
[a-zA-Z_:][a-zA-Z0-9_:]*,禁止出现-、空格、中文;标签值不能含换行或\,否则解析失败静默丢弃
gRPC服务怎么加监控?别重复造轮子,用go-grpc-prometheus
HTTP服务暴露/metrics是标准路径,但gRPC没有HTTP handler,也不能直接返回文本指标。硬写StatsHandler+自定义metric上报极易漏掉流式调用、错误分类、延迟分位数等关键维度。
立即学习“go语言免费学习笔记(深入)”;
- 直接用
github.com/grpc-ecosystem/go-grpc-prometheus:它内置ServerMetrics和ClientMetrics,自动采集grpc_server_handled_total、grpc_server_handling_seconds等标准指标 - 服务端只需两步:
metrics := grpc_prometheus.NewServerMetrics()→server := grpc.NewServer(grpc.StatsHandler(metrics))→metrics.Register() - 客户端加拦截器:
grpc.WithUnaryInterceptor(clientMetrics.UnaryClientInterceptor()),就能拿到grpc_client_handled_total等对端视角指标 - 注意:v2版本已弃用
EnableHandlingTimeHistogram(),改用WithHistogramBuckets()显式配置分位桶,否则延迟直方图为空
真正麻烦的从来不是“怎么加监控”,而是“加了之后谁看、怎么看、看懂了怎么行动”。比如go_goroutines突增50%——是泄漏还是瞬时压测?http_request_duration_seconds_bucket第99分位飙升——该扩实例还是优化SQL?这些都需要结合业务上下文打标(如handler、status_code、db_type)和告警抑制规则,而不是堆指标数量。


















