玩转PROMETHEUS(6) 实现自定义的Collector
版权声明 本站原创文章 由 萌叔 发表
转载请注明 萌叔 | https://vearne.cc
1. 前言
prometheus的官方和社区为了我们提供了丰富的exporter。对常见的硬件设备、数据库、消息队列以及常见的软件进行监控。另外官方还为我们提供了4种指标类型方便我们自定义exporter
Counter
Counter
代表累计指标,它表示单调递增的计数器,通常用于表示服务请求数,完成的任务数,错误的数量。Gauge
Gauge
表示某种瞬时状态,某一时刻的内存使用率、消息队列中的消息数量等等。它的值既可以增大,也可以减小。Histogram
通常用于top percentile,比如请求耗时的TP90、TP99等Summary
类似于Histogram
我们回顾一下prometheus的指标采集的一般过程
1) 创建指标
HTTPReqTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
Name: "http_requests_total",
Help: "Total number of HTTP requests made.",
}, []string{"method", "path", "status"})
2) 指标注册到 DefaultRegisterer
prometheus.MustRegister(
HTTPReqTotal,
)
3) 指标和对应的值通过HTTP API暴露出来
The caller of the Gather method can then expose the gathered metrics in some way. Usually, the metrics are served via HTTP on the /metrics endpoint.
http.Handle("/metrics", promhttp.Handler())
当promhttp.Handler()被执行时,所有metric
被序列化输出。题外话,其实输出的格式既可以是plain text,也可以是protocol Buffers。
这里涉及一个问题,这些metric的值是什么时候采集的?
2. 指标采集的触发时机
Counter
和Histogram
的触发时机比较明确
比如: 为了监控QPS, 请求完成以后,请求数量直接+1
HTTPReqTotal.With(prometheus.Labels{
PromLabelMethod: "GET",
PromLabelPath: "/api/query",
PromLabelStatus: "200",
}).Inc()
比如: 为了监控请求耗时分布,请求完成以后,只需
HTTPReqDuration.With(prometheus.Labels{
PromLabelMethod: "GET",
PromLabelPath: "/api/query",
}).Observe(3.15)
对于 Gauge
这种瞬时值,该怎么办?
假定我们要监控某个channel的长度
var jobChannel chan int
2.1 方法1-定时任务
jobChanLength:= prometheus.NewGauge(prometheus.GaugeOpts{
Name: "job_channel_length",
Help: "length of job channel",
})
go func() {
jobChanLength.Set(float64(len(jobChannel)))
c := time.Tick(30 * time.Second)
for range c {
jobChanLength.Set(float64(len(jobChannel)))
}
}()
2.2 方法2-自定义Collector
example2.go
实现Collector接口
type Collector interface {
Describe(chan<- *Desc)
// 比如满足并发安全
Collect(chan<- Metric)
}
Collect is called by the Prometheus registry when collecting metrics
当promhttp.Handler()被调用是,Collect函数被执行,所以此函数不能运行时间过长,否则可能导致promhttp.Handler()执行超时。
type ChanCollector struct{
JobChannel chan int
Name string
}
func NewChanCollector(jobChannel chan int, name string) *ChanCollector{
c := ChanCollector{}
c.JobChannel = jobChannel
c.Name = name
return &c
}
func (c *ChanCollector) Describe(ch chan<- *prometheus.Desc) {
desc := prometheus.NewDesc(c.Name, "length of channel", nil, nil)
ch <- desc
}
func (c *ChanCollector) Collect(ch chan<- prometheus.Metric) {
ch <- prometheus.MustNewConstMetric(
prometheus.NewDesc(c.Name, "length of channel", nil, nil),
prometheus.GaugeValue,
float64(len(c.JobChannel)),
)
}
collector := NewChanCollector(jobChannel, "job_channel_length")
prometheus.MustRegister(collector)
2.3 方法3-利用GaugeFunc实现
jobChanLength := prometheus.NewGaugeFunc(prometheus.GaugeOpts{
Name: "job_channel_length",
Help: "length of job channel",
},func() float64{
return float64(len(jobChannel))
})
prometheus.MustRegister(jobChanLength)
只需要定义函数 func()float64{} 即可
当promhttp.Handler()被调用时,func()float64{} 被触发。
3. 总结
如果执行速度很快,直接使用方法3就可以; 但是如果执行速度很快且输出的指标很多,推荐使用方法2; 其它情况推荐使用方法1。
func (c *ChanCollector) Collect(ch chan<- prometheus.Metric) 应该是
func (c *ChanCollector) Collect(ch chan<- *prometheus.Metric)
错了
https://github.com/prometheus/client_golang/blob/main/prometheus/collector.go#L62
已经核对过了,没有错
感谢作者