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模塊每三分鐘去數據庫里拉一次數據。

相關文章
相關標籤/搜索