package main import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" "net/http" "sync" ) type ClusterManager struct { sync.Mutex Zone string metricMapCounters map[string]string metricMapGauges map[string]string } // Simulate prepare the data func (c *ClusterManager) ReallyExpensiveAssessmentOfTheSystemState() ( metrics map[string]float64, ) { metrics = map[string]float64{ "oom_crashes_total": 42.00, "ram_usage": 6.023e23, } return } // 通过NewClusterManager方法创建结构体及对应的指标信息,代码如下所示。 // NewClusterManager creates the two Descs OOMCountDesc and RAMUsageDesc. Note // that the zone is set as a ConstLabel. (It's different in each instance of the // ClusterManager, but constant over the lifetime of an instance.) Then there is // a variable label "host", since we want to partition the collected metrics by // host. Since all Descs created in this way are consistent across instances, // with a guaranteed distinction by the "zone" label, we can register different // ClusterManager instances with the same registry. func NewClusterManager(zone string) *ClusterManager { return &ClusterManager{ Zone: zone, metricMapGauges: map[string]string{ "ram_usage": "ram_usage_bytes", }, metricMapCounters: map[string]string{ "oom_crashes": "oom_crashes_total", }, } } //首先,采集器必须实现prometheus.Collector接口,也必须实现Describe和Collect方法。实现接口的代码如下所示。 // Describe simply sends the two Descs in the struct to the channel. // Prometheus的注册器调用Collect来抓取参数 // 将收集的数据传递到Channel中并返回 // 收集的指标信息来自Describe,可以并发地执行抓取工作,但是必须要保证线程的安全 func (c *ClusterManager) Describe(ch chan<- *prometheus.Desc) { // prometheus.NewDesc(prometheus.BuildFQName(namespace, "", metricName), docString, labels, nil) for _, v := range c.metricMapGauges { ch <- prometheus.NewDesc(prometheus.BuildFQName(c.Zone, "", v), v, nil, nil) } for _, v := range c.metricMapCounters { ch <- prometheus.NewDesc(prometheus.BuildFQName(c.Zone, "", v), v, nil, nil) } } // Collect方法是核心,它会抓取你需要的所有数据,根据需求对其进行分析,然后将指标发送回客户端库。 // 用于传递所有可能指标的定义描述符 // 可以在程序运行期间添加新的描述,收集新的指标信息 // 重复的描述符将被忽略。两个不同的Collector不要设置相同的描述符 func (c *ClusterManager) Collect(ch chan<- prometheus.Metric) { c.Lock() defer c.Unlock() m := c.ReallyExpensiveAssessmentOfTheSystemState() for k, v := range m { t := prometheus.GaugeValue if c.metricMapCounters[k] != "" { t = prometheus.CounterValue } c.registerConstMetric(ch, k, v, t) } } // 用于传递所有可能指标的定义描述符给指标 func (c *ClusterManager) registerConstMetric(ch chan<- prometheus.Metric, metric string, val float64, valType prometheus.ValueType, labelValues ...string) { descr := prometheus.NewDesc(prometheus.BuildFQName(c.Zone, "", metric), metric, nil, nil) if m, err := prometheus.NewConstMetric(descr, valType, val, labelValues...); err == nil { ch <- m } } func main() { workerCA := NewClusterManager("xiaodian") reg := prometheus.NewPedanticRegistry() reg.MustRegister(workerCA) //当promhttp.Handler()被执行时,所有metric被序列化输出。题外话,其实输出的格式既可以是plain text,也可以是protocol Buffers。 http.Handle("/metrics", promhttp.HandlerFor(reg, promhttp.HandlerOpts{})) http.ListenAndServe(":9100", nil) }