- 保證Go語(yǔ)言安裝環(huán)境(1.7以上)
1) 下載對(duì)應(yīng)的prometheus包
go get github.com/prometheus/client_golang/prometheus/promhttp
2)程序主函數(shù):
package main
import (
"log"
"net/http"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
func main() {
http.Handle("/metrics", promhttp.Handler())
log.Fatal(http.ListenAndServe(":8080", nil))
}
- 指標(biāo)類(lèi)別
Prometheus中主要使用的四類(lèi)指標(biāo)類(lèi)型,如下所示
- Counter (累加指標(biāo))
- Gauge (測(cè)量指標(biāo))
- Summary (概略圖)
- Histogram (直方圖)
Counter 一個(gè)累加指標(biāo)數(shù)據(jù),這個(gè)值隨著時(shí)間只會(huì)逐漸的增加航瞭,比如程序完成的總?cè)蝿?wù)數(shù)量瞒瘸,運(yùn)行錯(cuò)誤發(fā)生的總次數(shù)双戳。常見(jiàn)的還有交換機(jī)中snmp采集的數(shù)據(jù)流量也屬于該類(lèi)型承粤,代表了持續(xù)增加的數(shù)據(jù)包或者傳輸字節(jié)累加值虑乖。
Gauge代表了采集的一個(gè)單一數(shù)據(jù)懦趋,這個(gè)數(shù)據(jù)可以增加也可以減少,比如CPU使用情況疹味,內(nèi)存使用量仅叫,硬盤(pán)當(dāng)前的空間容量等等
Histogram和Summary使用的頻率較少,兩種都是基于采樣的方式糙捺。另外有一些庫(kù)對(duì)于這兩個(gè)指標(biāo)的使用和支持程度不同诫咱,有些僅僅實(shí)現(xiàn)了部分功能。這兩個(gè)類(lèi)型對(duì)于某一些業(yè)務(wù)需求可能比較常見(jiàn)洪灯,比如查詢(xún)單位時(shí)間內(nèi):總的響應(yīng)時(shí)間低于300ms的占比坎缭,或者查詢(xún)95%用戶(hù)查詢(xún)的門(mén)限值對(duì)應(yīng)的響應(yīng)時(shí)間是多少。 使用Histogram和Summary指標(biāo)的時(shí)候同時(shí)會(huì)產(chǎn)生多組數(shù)據(jù)签钩,_count代表了采樣的總數(shù)掏呼,_sum則代表采樣值的和。 _bucket則代表了落入此范圍的數(shù)據(jù)铅檩。
下面是使用historam來(lái)定義的一組指標(biāo)憎夷,計(jì)算出了平均五分鐘內(nèi)的查詢(xún)請(qǐng)求小于0.3s的請(qǐng)求占比總量的比例值。
sum(rate(http_request_duration_seconds_bucket{le="0.3"}[5m])) by (job)
/
sum(rate(http_request_duration_seconds_count[5m])) by (job)
如果需要聚合數(shù)據(jù)昧旨,可以使用histogram. 并且如果對(duì)于分布范圍有明確的值的情況下(比如300ms)拾给,也可以使用histogram。但是如果僅僅是一個(gè)百分比的值(比如上面的95%),則使用Summary
- 定義指標(biāo)
這里我們需要引入另一個(gè)依賴(lài)庫(kù)
go get github.com/prometheus/client_golang/prometheus
下面先來(lái)定義了兩個(gè)指標(biāo)數(shù)據(jù),一個(gè)是Guage類(lèi)型宁舰, 一個(gè)是Counter類(lèi)型。分別代表了CPU溫度和磁盤(pán)失敗次數(shù)統(tǒng)計(jì)额衙,使用上面的定義進(jìn)行分類(lèi)。
cpuTemp = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "cpu_temperature_celsius",
Help: "Current temperature of the CPU.",
})
hdFailures = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "hd_errors_total",
Help: "Number of hard-disk errors.",
},
[]string{"device"},
)
這里還可以注冊(cè)其他的參數(shù),比如上面的磁盤(pán)失敗次數(shù)統(tǒng)計(jì)上入偷,我們可以同時(shí)傳遞一個(gè)device設(shè)備名稱(chēng)進(jìn)去追驴,這樣我們采集的時(shí)候就可以獲得多個(gè)不同的指標(biāo)。每個(gè)指標(biāo)對(duì)應(yīng)了一個(gè)設(shè)備的磁盤(pán)失敗次數(shù)統(tǒng)計(jì)疏之。
- 注冊(cè)指標(biāo)
func init() {
// Metrics have to be registered to be exposed:
prometheus.MustRegister(cpuTemp)
prometheus.MustRegister(hdFailures)
}
使用prometheus.MustRegister是將數(shù)據(jù)直接注冊(cè)到Default Registry,就像上面的運(yùn)行的例子一樣暇咆,這個(gè)Default Registry不需要額外的任何代碼就可以將指標(biāo)傳遞出去锋爪。注冊(cè)后既可以在程序?qū)用嫔先ナ褂迷撝笜?biāo)了,這里我們使用之前定義的指標(biāo)提供的API(Set和With().Inc)去改變指標(biāo)的數(shù)據(jù)內(nèi)容
func main() {
cpuTemp.Set(65.3)
hdFailures.With(prometheus.Labels{"device":"/dev/sda"}).Inc()
// The Handler function provides a default handler to expose metrics
// via an HTTP server. "/metrics" is the usual endpoint for that.
http.Handle("/metrics", promhttp.Handler())
log.Fatal(http.ListenAndServe(":8080", nil))
}
其中With函數(shù)是傳遞到之前定義的label=”device”上的值爸业,也就是生成指標(biāo)類(lèi)似于
cpu_temperature_celsius 65.3
hd_errors_total{"device"="/dev/sda"} 1
當(dāng)然我們寫(xiě)在main函數(shù)中的方式是有問(wèn)題的其骄,這樣這個(gè)指標(biāo)僅僅改變了一次,不會(huì)隨著我們下次采集數(shù)據(jù)的時(shí)候發(fā)生任何變化扯旷,我們希望的是每次執(zhí)行采集的時(shí)候拯爽,程序都去自動(dòng)的抓取指標(biāo)并將數(shù)據(jù)通過(guò)http的方式傳遞給我們。
- Counter數(shù)據(jù)采集實(shí)例
下面是一個(gè)采集Counter類(lèi)型數(shù)據(jù)的實(shí)例钧忽,這個(gè)例子中實(shí)現(xiàn)了一個(gè)自定義的毯炮,滿足采集器(Collector)接口的結(jié)構(gòu)體,并手動(dòng)注冊(cè)該結(jié)構(gòu)體后耸黑,使其每次查詢(xún)的時(shí)候自動(dòng)執(zhí)行采集任務(wù)桃煎。
我們先來(lái)看下采集器Collector接口的實(shí)現(xiàn)
type Collector interface {
// 用于傳遞所有可能的指標(biāo)的定義描述符
// 可以在程序運(yùn)行期間添加新的描述,收集新的指標(biāo)信息
// 重復(fù)的描述符將被忽略大刊。兩個(gè)不同的Collector不要設(shè)置相同的描述符
Describe(chan<- *Desc)
// Prometheus的注冊(cè)器調(diào)用Collect執(zhí)行實(shí)際的抓取參數(shù)的工作为迈,
// 并將收集的數(shù)據(jù)傳遞到Channel中返回
// 收集的指標(biāo)信息來(lái)自于Describe中傳遞,可以并發(fā)的執(zhí)行抓取工作缺菌,但是必須要保證線程的安全葫辐。
Collect(chan<- Metric)
}
了解了接口的實(shí)現(xiàn)后,我們就可以寫(xiě)自己的實(shí)現(xiàn)了伴郁,先定義結(jié)構(gòu)體耿战,這是一個(gè)集群的指標(biāo)采集器,每個(gè)集群都有自己的Zone,代表集群的名稱(chēng)蛾绎。另外兩個(gè)是保存的采集的指標(biāo)昆箕。
type ClusterManager struct {
Zone string
OOMCountDesc *prometheus.Desc
RAMUsageDesc *prometheus.Desc
}
我們來(lái)實(shí)現(xiàn)一個(gè)采集工作,放到了ReallyExpensiveAssessmentOfTheSystemState函數(shù)中實(shí)現(xiàn),每次執(zhí)行的時(shí)候租冠,返回一個(gè)按照主機(jī)名作為鍵采集到的數(shù)據(jù)鹏倘,兩個(gè)返回值分別代表了OOM錯(cuò)誤計(jì)數(shù),和RAM使用指標(biāo)信息顽爹。
func (c *ClusterManager) ReallyExpensiveAssessmentOfTheSystemState() (
oomCountByHost map[string]int, ramUsageByHost map[string]float64,
) {
oomCountByHost = map[string]int{
"foo.example.org": int(rand.Int31n(1000)),
"bar.example.org": int(rand.Int31n(1000)),
}
ramUsageByHost = map[string]float64{
"foo.example.org": rand.Float64() * 100,
"bar.example.org": rand.Float64() * 100,
}
return
}
實(shí)現(xiàn)Describe接口纤泵,傳遞指標(biāo)描述符到channel
// Describe simply sends the two Descs in the struct to the channel.
func (c *ClusterManager) Describe(ch chan<- *prometheus.Desc) {
ch <- c.OOMCountDesc
ch <- c.RAMUsageDesc
}
Collect函數(shù)將執(zhí)行抓取函數(shù)并返回?cái)?shù)據(jù),返回的數(shù)據(jù)傳遞到channel中,并且傳遞的同時(shí)綁定原先的指標(biāo)描述符捏题。以及指標(biāo)的類(lèi)型(一個(gè)Counter和一個(gè)Guage)
func (c *ClusterManager) Collect(ch chan<- prometheus.Metric) {
oomCountByHost, ramUsageByHost := c.ReallyExpensiveAssessmentOfTheSystemState()
for host, oomCount := range oomCountByHost {
ch <- prometheus.MustNewConstMetric(
c.OOMCountDesc,
prometheus.CounterValue,
float64(oomCount),
host,
)
}
for host, ramUsage := range ramUsageByHost {
ch <- prometheus.MustNewConstMetric(
c.RAMUsageDesc,
prometheus.GaugeValue,
ramUsage,
host,
)
}
}
創(chuàng)建結(jié)構(gòu)體及對(duì)應(yīng)的指標(biāo)信息,NewDesc參數(shù)第一個(gè)為指標(biāo)的名稱(chēng)玻褪,第二個(gè)為幫助信息,顯示在指標(biāo)的上面作為注釋?zhuān)谌齻€(gè)是定義的label名稱(chēng)數(shù)組公荧,第四個(gè)是定義的Labels
func NewClusterManager(zone string) *ClusterManager {
return &ClusterManager{
Zone: zone,
OOMCountDesc: prometheus.NewDesc(
"clustermanager_oom_crashes_total",
"Number of OOM crashes.",
[]string{"host"},
prometheus.Labels{"zone": zone},
),
RAMUsageDesc: prometheus.NewDesc(
"clustermanager_ram_usage_bytes",
"RAM usage as reported to the cluster manager.",
[]string{"host"},
prometheus.Labels{"zone": zone},
),
}
}
執(zhí)行主程序
func main() {
workerDB := NewClusterManager("db")
workerCA := NewClusterManager("ca")
// Since we are dealing with custom Collector implementations, it might
// be a good idea to try it out with a pedantic registry.
reg := prometheus.NewPedanticRegistry()
reg.MustRegister(workerDB)
reg.MustRegister(workerCA)
}
如果直接執(zhí)行上面的參數(shù)的話带射,不會(huì)獲取任何的參數(shù),因?yàn)槌绦驅(qū)⒆詣?dòng)推出循狰,我們并未定義http接口去暴露數(shù)據(jù)出來(lái)窟社,因此數(shù)據(jù)在執(zhí)行的時(shí)候還需要定義一個(gè)httphandler來(lái)處理http請(qǐng)求。
添加下面的代碼到main函數(shù)后面绪钥,即可實(shí)現(xiàn)數(shù)據(jù)傳遞到http接口上:
gatherers := prometheus.Gatherers{
prometheus.DefaultGatherer,
reg,
}
h := promhttp.HandlerFor(gatherers,
promhttp.HandlerOpts{
ErrorLog: log.NewErrorLogger(),
ErrorHandling: promhttp.ContinueOnError,
})
http.HandleFunc("/metrics", func(w http.ResponseWriter, r *http.Request) {
h.ServeHTTP(w, r)
})
log.Infoln("Start server at :8080")
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Errorf("Error occur when start server %v", err)
os.Exit(1)
}
其中prometheus.Gatherers用來(lái)定義一個(gè)采集數(shù)據(jù)的收集器集合灿里,可以merge多個(gè)不同的采集數(shù)據(jù)到一個(gè)結(jié)果集合,這里我們傳遞了缺省的DefaultGatherer程腹,所以他在輸出中也會(huì)包含go運(yùn)行時(shí)指標(biāo)信息匣吊。同時(shí)包含reg是我們之前生成的一個(gè)注冊(cè)對(duì)象,用來(lái)自定義采集數(shù)據(jù)寸潦。
promhttp.HandlerFor()函數(shù)傳遞之前的Gatherers對(duì)象色鸳,并返回一個(gè)httpHandler對(duì)象,這個(gè)httpHandler對(duì)象可以調(diào)用其自身的ServHTTP函數(shù)來(lái)接手http請(qǐng)求甸祭,并返回響應(yīng)缕碎。其中promhttp.HandlerOpts定義了采集過(guò)程中如果發(fā)生錯(cuò)誤時(shí),繼續(xù)采集其他的數(shù)據(jù)池户。
嘗試刷新幾次瀏覽器獲取最新的指標(biāo)信息
clustermanager_oom_crashes_total{host="bar.example.org",zone="ca"} 364
clustermanager_oom_crashes_total{host="bar.example.org",zone="db"} 90
clustermanager_oom_crashes_total{host="foo.example.org",zone="ca"} 844
clustermanager_oom_crashes_total{host="foo.example.org",zone="db"} 801
# HELP clustermanager_ram_usage_bytes RAM usage as reported to the cluster manager.
# TYPE clustermanager_ram_usage_bytes gauge
clustermanager_ram_usage_bytes{host="bar.example.org",zone="ca"} 10.738111282075208
clustermanager_ram_usage_bytes{host="bar.example.org",zone="db"} 19.003276633920805
clustermanager_ram_usage_bytes{host="foo.example.org",zone="ca"} 79.72085409108028
clustermanager_ram_usage_bytes{host="foo.example.org",zone="db"} 13.041384617379178
每次刷新的時(shí)候咏雌,我們都會(huì)獲得不同的數(shù)據(jù),類(lèi)似于實(shí)現(xiàn)了一個(gè)數(shù)值不斷改變的采集器校焦。當(dāng)然赊抖,具體的指標(biāo)和采集函數(shù)還需要按照需求進(jìn)行修改,滿足實(shí)際的業(yè)務(wù)需求寨典。
- 添加自己的時(shí)間戳(NewMetricWithTimestamp)
因?yàn)闃I(yè)務(wù)需要氛雪,數(shù)據(jù)上報(bào)需要嚴(yán)格按照自己的時(shí)間戳進(jìn)行數(shù)據(jù)的處理
desc := prometheus.NewDesc(
"temperature_kelvin",
"Current temperature in Kelvin.",
nil, nil,
)
// Create a constant gauge from values we got from an external
// temperature reporting system. Those values are reported with a slight
// delay, so we want to add the timestamp of the actual measurement.
temperatureReportedByExternalSystem := 298.15
timeReportedByExternalSystem := time.Date(2009, time.November, 10, 23, 0, 0, 12345678, time.UTC)
s := prometheus.NewMetricWithTimestamp(
timeReportedByExternalSystem,
prometheus.MustNewConstMetric(
desc, prometheus.GaugeValue, temperatureReportedByExternalSystem,
),
)
// Just for demonstration, let's check the state of the gauge by
// (ab)using its Write method (which is usually only used by Prometheus
// internally).
metric := &dto.Metric{}
s.Write(metric)
fmt.Println(proto.MarshalTextString(metric))
參考資料:https://blog.csdn.net/u014029783/article/details/80001251
https://godoc.org/github.com/prometheus/client_golang/prometheus#NewMetricWithTimestamp