不少時候,咱們在使用Prometheus時,官方提供的採集組件不能知足監控需求,咱們就須要自行編寫Exporter。git
本文的示例採用go語言和Gauge (測量指標)類型實現。自定義Exporter去取MongoDB裏動態增加的數據。github
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
須要先引入這個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施加的全局狀態,能夠同時使用多個註冊表,以不一樣的方式公開不一樣的指標, 也能夠將單獨的註冊表用於測試目的。
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模塊每三分鐘去數據庫里拉一次數據。