进阶自定义prometheus exporter

package utils

import (
    "fmt"
    "net/http"

    "github.com/prometheus/client_golang/prometheus"
    "github.com/prometheus/client_golang/prometheus/promhttp"
    "github.com/prometheus/common/log"
)
// 最终生成的指标数据
// # HELP dns_query_count_total Count of DNS Query.
// # TYPE dns_query_count_total counter
// dns_query_count_total{kkkk="self",net="udp",query_proto="dns","query_help": "queryHelp", "xxxxxx": "queryHelp"} 999999
 
 
// 1. 定义一个结构体,用于存放描述信息
type Exporter struct {
    queryCountDesc *prometheus.Desc
}

// 2. 定义一个Collector接口,用于存放两个必备函数,Describe和Collect
type Collector interface {
    Describe(chan<- *prometheus.Desc)
    Collect(chan<- prometheus.Metric)
}

// 3. 定义两个必备函数Describe和Collect
func (e *Exporter) Describe(ch chan<- *prometheus.Desc) {
    // 将描述信息放入队列
    ch <- e.queryCountDesc
}

func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
    //  采集业务指标数据
    queryCount := e.QueryState() // 获取指标数据值
    ch <- prometheus.MustNewConstMetric(
        e.queryCountDesc,        // 将指标数据与自定义描述信息绑定
        prometheus.CounterValue, // 指定该指标数据的类型,这里表示counter类型
        float64(queryCount),     // 指标数据的值转为float64类型,必须的
        "dns",                   // 指标的标签值,多个标签值的话,继续往后写即可。与NewExporter中prometheus.NewDesc的第三个参数
        "udp",                   // 是一一对应的。这里的dns就是query_proto的值
        "self",
    )
}

// 4. 自定义一个生成业务指标数据的函数
func (e *Exporter) QueryState() (queryCount float64) {
    queryCount = 999999
    return
}

// 5. 定义一个实例化函数,用于生成prometheus数据
func NewExporter() *Exporter {
    return &Exporter{
        queryCountDesc: prometheus.NewDesc(
            "dns_query_count_total",                // 自定义指标名称
            "Count of DNS Query.",                  // 指标的help信息
            []string{"query_proto", "net", "kkkk"}, // 指标的标签名称,多个就写到数组里,与Collect中prometheus.MustNewConstMetric最后的几个标签值是一一对应的
            prometheus.Labels{"query_help": "queryHelp", "xxxxxx": "queryHelp"}, // 也是定义指标标签名称,不过通常用于定义全局唯一的指标标签名称,用的少一些。
            // 这个不是必须的, 不需要的话,可以写为nil。
        ),
    }
}

func init() {
    // 6. 实例化并注册数据采集器exporter
    workerA := NewExporter()
    reg := prometheus.NewPedanticRegistry()
    reg.MustRegister(workerA)

    // 7. 定义一个采集数据的采集器集合,它可以合并多个不同的采集器数据到一个结果集合中
    gatherers := prometheus.Gatherers{
        // prometheus.DefaultGatherer,  // 默认的数据采集器,包含go运行时的指标信息
        reg, // 自定义的采集器
    }

    // 8. 启动http服务
    h := promhttp.HandlerFor(gatherers,
        // HandlerFor函数传递上边的gatherers对象,并返回一个httpHandler对象h。
        // 这个httpHandler对象h可以调用其自身的ServHTTP函数来接收HTTP请求,并返回响应
        promhttp.HandlerOpts{
            ErrorLog:      log.NewErrorLogger(),     // 采集过程中如果出现错误,记录日志
            ErrorHandling: promhttp.ContinueOnError, // 采集过程中如果出现错误,继续采集其他数据,不会中断采集器的工作
        })
    http.HandleFunc("/metrics", func(w http.ResponseWriter, r *http.Request) {
        fmt.Println("start...")
        h.ServeHTTP(w, r)
    })
    log.Fatal(http.ListenAndServe(":9999", nil))
}