不少时候,咱们在使用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模块每三分钟去数据库里拉一次数据。