Prometheus自定义Exporter的实现

不少时候,咱们在使用Prometheus时,官方提供的采集组件不能知足监控需求,咱们就须要自行编写Exporter。git

本文的示例采用go语言和Gauge (测量指标)类型实现。自定义Exporter去取MongoDB里动态增加的数据。github

Metric接口

Prometheus client库提供了四种度量标准类型。golang

虽然只有基本度量标准类型实现Metric接口,可是度量标准及其向量版本都实现了Collector接口。Collector管理许多度量标准的收集,但为方便起见,度量标准也能够"自行收集"。Prometheus数据模型的一个很是重要的部分是沿称为label的维度对样本进行划分,从而产生度量向量。基本类型是GaugeVec,CounterVec,SummaryVec和HistogramVec。注意,Gauge,Counter,Summary和Histogram自己是接口,而GaugeVec,CounterVec,SummaryVec和HistogramVec则不是接口。mongodb

  • Counter (累积)数据库

    Counter通常表示一个单调递增的计数器。json

  • Gauge (测量)api

    Gauge通常用于表示可能动态变化的单个值,可能增大也可能减少。安全

  • Histogram (直方图)并发

  • Summary (概略图)app

注册指标并启动HTTP服务

须要先引入这个Prometheus client库。一般metric endpoint使用http来暴露metric,经过http暴露metric的工具为promhttp子包,引入promhttp和net/http。

"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang//prometheus/promhttp"

Registry/Register/Gatherers及http服务

func main() {
	daystr := time.Now().Format("20060102")
	logFile, err := os.Create("./exporter/log/" + daystr + ".txt")
	defer logFile.Close()
	if err != nil {
		fmt.Printf("%v\n", err)
		return
	}
	logger := log.New(logFile, "Prefix_", log.Ldate|log.Ltime|log.Lshortfile)
	
	//Registry和Register部分
	reg := prometheus.NewPedanticRegistry()
	reg.MustRegister(metrics.initCollector())    //metrics.initCollector()是collector初始化模块
	//如下是官方文档定义
	// Register registers a new Collector to be included in metrics
	// collection. It returns an error if the descriptors provided by the
	// Collector are invalid or if they — in combination with descriptors of
	// already registered Collectors — do not fulfill the consistency and
	// uniqueness criteria described in the documentation of metric.Desc.
	//
	// If the provided Collector is equal to a Collector already registered
	// (which includes the case of re-registering the same Collector), the
	// returned error is an instance of AlreadyRegisteredError, which
	// contains the previously registered Collector.
	//
	// A Collector whose Describe method does not yield any Desc is treated
	// as unchecked. Registration will always succeed. No check for
	// re-registering (see previous paragraph) is performed. Thus, the
	// caller is responsible for not double-registering the same unchecked
	// Collector, and for providing a Collector that will not cause
	// inconsistent metrics on collection. (This would lead to scrape
	// errors.)

	// MustRegister works like Register but registers any number of
	// Collectors and panics upon the first registration that causes an
	// error.

	//Gatherers部分
	gatherers := prometheus.Gatherers{
		reg,
	}
	//如下是官方文档定义
	// Gatherers is a slice of Gatherer instances that 
	// implements the Gatherer interface itself.
	// Its Gather method calls Gather on all Gatherers 
	// in the slice in order and returns the merged results.
	// Errors returned from the Gather calls are 
	// all returned in a flattened MultiError.
	// Duplicate and inconsistent Metrics are 
	// skipped (first occurrence in slice order wins) 
	// and reported in the returned error.

	//注册http服务
	h := promhttp.HandlerFor(gatherers,
		promhttp.HandlerOpts{
			ErrorHandling: promhttp.ContinueOnError,
		})
	http.HandleFunc("/metrics", func(w http.ResponseWriter, r *http.Request) {
		h.ServeHTTP(w, r)
	})
	log.Println("Start server at :8710")
	logger.Printf("Start server at :8710")

	if err := http.ListenAndServe(":8710", nil); err != nil {
		log.Printf("Error occur when start server %v", err)
		logger.Printf("Error occur when start server %v", err)
		os.Exit(1)
	}		
}

Registry是为了根据Prometheus数据模型来确保所收集指标的一致性。若是注册的Collector与注册的Metrics不兼容或不一致,则返回错误。理想状况下,在注册时而不是在收集时检测到不一致。对注册的Collector的检测一般会在程序启动时被检测到,而对注册的Metrics的检测只会在抓取时发生,这就是Collector和Metrics必须向Registry描述本身的主要缘由。

Registry实现了Gatherer接口。 而后Gather方法的调用者能够某种方式公开收集的指标。一般度量是经过/metrics端点上的HTTP提供的。在上面的示例中就是这种状况。经过HTTP公开指标的工具位于promhttp子软件包中。

NewPedanticRegistry能够避免由DefaultRegisterer施加的全局状态,能够同时使用多个注册表,以不一样的方式公开不一样的指标, 也能够将单独的注册表用于测试目的。

实现Collector接口

type Collector interface {
    // 用于传递全部可能的指标的定义描述符
    // 能够在程序运行期间添加新的描述,收集新的指标信息
    // 重复的描述符将被忽略。两个不一样的Collector不要设置相同的描述符
    Describe(chan<- *Desc)

    // Prometheus的注册器调用Collect执行实际的抓取参数的工做,
    // 并将收集的数据传递到Channel中返回
    // 收集的指标信息来自于Describe中传递,能够并发的执行抓取工做,可是必需要保证线程的安全。
    Collect(chan<- Metric)
}

在另外的模块中编写Collector以及其初始化的代码。

先定义一个结构体,指标使用的是 prometheus的Desc类型。

type ProjectMetrics struct {
	MetricsDescs []*prometheus.Desc
}

定义MetricsName和 MetricsHelp。

var MetricsNameXXX = "XXX"
var MetricsHelpXXX = "(XXX)"

对自定义类型ProjectMetrics定义Describe方法和Collect方法来实现实现Collector接口。

func (c *ProjectMetrics) Describe(ch chan<- *prometheus.Desc) {
	len1 := len(c.MetricsDescs)
	for i := 0; i < len1; i++ {
		ch <- c.MetricsDescs[i]
	}
}

func (c *ProjectMetrics) Collect(ch chan<- prometheus.Metric) {
	start := time.Now()
	nowUTC := start.UTC()
	resp := mongodb.QueryAllData(nowUTC.Unix())
	for _, v := range resp.Data1Tier {
		item1 := v.Item1
		fmt.Println("......................", isp)
		ts := time.Unix(v.Clientutc, 0)
		for _, v2 := range v.Data2Tier {
			item2 := v2.Item2
			item3 := v2.Item3
			item4 := v2.Item4
			tmp := prometheus.NewDesc(
				MetricsNameXXX,
				MetricsHelpXXX,
				[]string{"Name"},
				prometheus.Labels{"item1": item1, "item3": item3, "item4": item4},
			)
			ch <- prometheus.NewMetricWithTimestamp(
				ts,
				prometheus.MustNewConstMetric(
					tmp,
					prometheus.GaugeValue,
					item2,
					MetricsNameXXX,
				),
			)
		}
	}
	eT := time.Since(start)
	fmt.Printf("Project Metrics, Elapsed Time: %s, Date(UTC): %s\n", eT, start.UTC().Format("2006/01/02T15:04:05"))
}

初始化ProjectMetrics,

func AddMetricsItem2() *ProjectMetrics {
	var tmpMetricsDescs []*prometheus.Desc
	resp := mongodb.QueryAllData(time.Now().UTC().Unix())
	for _, v := range resp.Data1Tier {
		item1 := v.Item1
		for _, v2 := range v.Data2Tier {

			item3 := v2.Item3
			item4 := v2.Item4
			tmp := prometheus.NewDesc(
				MetricsNameLatency,
				MetricsHelpLatency,
				[]string{"Name"},
				prometheus.Labels{"item1": item1, "item3": item3, "item4": item4},
			)
			tmpMetricsDescs = append(tmpMetricsDescs, tmp)
		}
	} //aws

	api := &ProjectMetrics{MetricsDescs: tmpMetricsDescs}
	return api
}

MongoDB中的数据,

type SensorData struct {
	Aaaa         string      `json:"aaaa"`
	Item2        float64     `json:"item2"`
	Item4            string      `json:"item4"`
	Item3 string      `json:"item3"`
	Bbbb          float64     `json:"bbbb"`
	Cccc         string      `json:"cccc"`
	Dddd         []CmdResult `json:"dddd"`
	Eeee           string      `json:"eeee"`
}

type Sensor struct {
	Item1         string `json:"item1"`
	Clientutc   int64  `json:"clientutc"`
	Data2Tier []SensorData
}

type AllData struct {
	Data1Tier []Sensor
}

mongodb.QueryAllData()是另外从mongodb中拉取数据的模块,返回AllData类型数据。mongodb模块每三分钟去数据库里拉一次数据。

相关文章
相关标签/搜索