From 6afa1456943afc7a062e5df7cf324f6a2b67bf85 Mon Sep 17 00:00:00 2001 From: sre Date: Thu, 14 Jul 2022 16:20:13 +0800 Subject: [PATCH] add simple exporter --- main.go | 2 +- main.go.new.txt | 97 +++++++++++++++++++++++++++++++++++++++++++++++++ main.simple.txt | 47 ++++++++++++++++++++++++ 3 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 main.go.new.txt create mode 100644 main.simple.txt diff --git a/main.go b/main.go index 34942b4..795839b 100644 --- a/main.go +++ b/main.go @@ -93,5 +93,5 @@ func main() { reg.MustRegister(workerCA) //当promhttp.Handler()被执行时,所有metric被序列化输出。题外话,其实输出的格式既可以是plain text,也可以是protocol Buffers。 http.Handle("/metrics", promhttp.HandlerFor(reg, promhttp.HandlerOpts{})) - http.ListenAndServe("9100", nil) + http.ListenAndServe(":9100", nil) } diff --git a/main.go.new.txt b/main.go.new.txt new file mode 100644 index 0000000..795839b --- /dev/null +++ b/main.go.new.txt @@ -0,0 +1,97 @@ +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) +} diff --git a/main.simple.txt b/main.simple.txt new file mode 100644 index 0000000..a069f1f --- /dev/null +++ b/main.simple.txt @@ -0,0 +1,47 @@ +package main + +//自己动手编写一个Exporter +//一般来说,绝大多数Exporter都是基于Go语言编写的,一小部分是基于Python语言编写的,还有很小一部分是使用Java语言编写的。比如官方提供的Consul Metrics自定义采集器Exporter,如果是在Go语言的运行环境下,需要按照如下所示代码运行这个Exporter。 + +import ( + "log" + "net/http" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" +) + +var ( + cpuTemp = prometheus.NewGauge(prometheus.GaugeOpts{ + Namespace: "our_idc", + Subsystem: "k8s", + Name: "cpu_temperature_celsius", + Help: "Current temperature of the CPU.", + }) + hdFailures = prometheus.NewCounterVec( + prometheus.CounterOpts{ + Namespace: "our_idc", + Subsystem: "k8s", + Name: "hd_errors_total", + Help: "Number of hard-disk errors.", + }, + []string{"device"}, + ) +) + +func init() { + // Metrics have to be registered to be exposed: + prometheus.MustRegister(cpuTemp) + prometheus.MustRegister(hdFailures) +} + +func main() { + cpuTemp.Set(65.3) + hdFailures.With(prometheus.Labels{"device": "/dev/sda"}).Inc() + + // The Handler function provides a default handler to expose metrics + // via an HTTP server. "/metrics" is the usual endpoint for that. + http.Handle("/metrics", promhttp.Handler()) + log.Fatal(http.ListenAndServe(":9105", nil)) + +}