实现一个prometheus的exporter示例
1,必须启动prometheus(prometheus以客户端的模式运行,然后自动周期去prometheus.yml中配置的exporter拉取数据),配置 prometheus.yml中exporter对应的promhttp的IP和端口,如果有改动配置文件,重启下prometheus程序;
2,启动自己的exporter,访问自己的exporter端口以及prometheus的task列表,都会有状态和数据变化。
3,启动grafana-6.7.6监控面板,配置数据源以及图标,即prometheus的exporter,就可以显示仪表盘。
开发exporter示例,Counter (累加指标) Gauge (测量指标) Summary (概略图) Histogram (直方图)
示例:
package main import ( "fmt" "net/http" ) func HelloHandler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "lexporter_request_count{user=\"admin\"} 1000" ) } func main () { http.HandleFunc("/metrics", HelloHandler) http.ListenAndServe(":8000", nil) }
基本:
package main import ( "fmt" "net/http" "runtime" "strings" "sync" "time" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/collectors" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/common/promlog" "github.com/prometheus/common/version" "github.com/go-kit/log" "github.com/go-kit/log/level" ) //var exporter = struct { // Name string // Type string //}{"terminal", "exporter"} var exporter = []string{"terminal", "exporter"} type nodeStatsMetrics []struct { desc *prometheus.Desc eval func(*runtime.MemStats) float64 valType prometheus.ValueType } type NodeCollector struct { counterDesc *prometheus.Desc //Counter hybridMetrics nodeStatsMetrics //hybrid gaugeDesc *prometheus.Desc //Gauge summaryDesc *prometheus.Desc //summary histogramDesc *prometheus.Desc //histogram mutex sync.Mutex } func NewNodeCollector() prometheus.Collector { return &NodeCollector{ counterDesc: prometheus.NewDesc( strings.Join(exporter, "_")+"_counter_desc", "terminal exporter counter desc", []string{"DYNAMIC_GOOS_NAME"}, prometheus.Labels{"STATIC_LABEL": "STATIC VALUE HERE", "GOOS": runtime.GOOS}), hybridMetrics: nodeStatsMetrics{ { desc: prometheus.NewDesc( strings.Join(exporter, "_")+"_mem_total", "terminal exporter ms.Sys", nil, nil), valType: prometheus.GaugeValue, eval: func(ms *runtime.MemStats) float64 { return float64(ms.Sys) / 1e9 }, }, { desc: prometheus.NewDesc( strings.Join(exporter, "_")+"_mem_heap", "terminal exporter ms.HeapSys", nil, nil), valType: prometheus.GaugeValue, eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapSys) / 1e9 }, }, }, gaugeDesc: prometheus.NewDesc( strings.Join(exporter, "_")+"_gauge_desc", "terminal exporter gaugeDesc", []string{"gauge"}, nil), summaryDesc: prometheus.NewDesc( strings.Join(exporter, "_")+"_summary_desc", "terminal exporter summaryDesc", []string{"code", "method"}, prometheus.Labels{"owner": "example"}, ), histogramDesc: prometheus.NewDesc( strings.Join(exporter, "_")+"_histogram_desc", "terminal exporter histogramDesc", []string{"code", "method"}, prometheus.Labels{"owner": "example"}, ), } } // Describe returns all descriptions of the collector. func (n *NodeCollector) Describe(ch chan<- *prometheus.Desc) { ch <- n.counterDesc for _, metric := range n.hybridMetrics { ch <- metric.desc } ch <- n.gaugeDesc ch <- n.summaryDesc ch <- n.histogramDesc } // Collect returns the current state of all metrics of the collector. func (n *NodeCollector) Collect(ch chan<- prometheus.Metric) { n.mutex.Lock() ch <- prometheus.MustNewConstMetric(n.counterDesc, prometheus.CounterValue, 0, runtime.GOOS) vm := &runtime.MemStats{} for _, metric := range n.hybridMetrics { ch <- prometheus.MustNewConstMetric(metric.desc, metric.valType, metric.eval(vm)) } num, _ := runtime.ThreadCreateProfile(nil) ch <- prometheus.MustNewConstMetric(n.gaugeDesc, prometheus.GaugeValue, float64(num), "thread", ) ch <- prometheus.MustNewConstMetric(n.gaugeDesc, prometheus.GaugeValue, float64(runtime.NumGoroutine()), "num") ch <- prometheus.MustNewConstSummary( n.summaryDesc, 4711, 403.34, map[float64]float64{0.5: 42.3, 0.9: 323.3}, "200", "get", ) ch <- prometheus.MustNewConstHistogram( n.histogramDesc, 4711, 403.34, map[float64]uint64{25: 121, 50: 2403, 100: 3221, 200: 4233}, "200", "get", ) n.mutex.Unlock() } var promRegistry = prometheus.NewRegistry() var promCounter = prometheus.NewCounter( prometheus.CounterOpts{ Name: "terminal_exporter_counter", Help: "Current terminal exporter counter.", }, ) var promGauge = prometheus.NewGauge( prometheus.GaugeOpts{ Name: "terminal_exporter_gauge", Help: "Unix timestamp of the last received metrics push in seconds.", }, ) var promTotal = prometheus.NewCounterVec( prometheus.CounterOpts{ Name: "terminal_exporter_total", Help: "Current terminal exporter total.", }, []string{"terminal"}, ) func init() { promRegistry.MustRegister( collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}), collectors.NewGoCollector(), collectors.NewBuildInfoCollector(), ) promRegistry.MustRegister(version.NewCollector("terminal_exporter")) promRegistry.MustRegister(promCounter) promRegistry.MustRegister(promGauge) promRegistry.MustRegister(promTotal) promRegistry.MustRegister(NewNodeCollector()) promGauge.Set(6.4) promCounter.Add(1) promTotal.With(prometheus.Labels{"terminal": "device"}).Add(5) promTotal.With(prometheus.Labels{"terminal": "device"}).Inc() promTotal.With(prometheus.Labels{"terminal": "xterm"}).Inc() } func main() { promlogConfig := &promlog.Config{} logger := promlog.New(promlogConfig) loger := log.NewLogfmtLogger(log.StdlibWriter{}) loger.Log("legacy", true, "msg", "NewLogfmtLogger StdlibWriter") loger = log.NewLogfmtLogger(log.NewStdlibAdapter(logger)) loger.Log("legacy", true, "msg", "NewLogfmtLogger NewStdlibAdapter") level.Info(logger).Log("msg", "Starting", "version", version.Info()) level.Info(logger).Log("msg", "Build context", "context", version.BuildContext()) ResponseWriteHeader := func(w http.ResponseWriter, status int) { if w == nil { return } //w.Header().Set("Content-Length", "2906") //w.Header().Set("Content-Type", "application/json; charset=utf-8") //w.Header().Set("X-Content-Type-Options", "nosniff") //w.WriteHeader(http.StatusOK) w.Header().Set("Content-Encoding", "gzip") w.Header().Set("Content-Type", "text/plain; version=0.0.4") w.WriteHeader(status) } ResponseWriteHeader(nil, 200) http.HandleFunc("/write", func(w http.ResponseWriter, r *http.Request) { fmt.Println("URL:", r.URL) fmt.Println("lastPush:", float64(time.Now().UnixNano())/1e9) fmt.Println("Content-Encoding:", r.Header.Get("Content-Encoding")) fmt.Println("Content-Type:", r.Header.Get("Content-Encoding")) }) http.HandleFunc("/api/v2/write", func(w http.ResponseWriter, r *http.Request) { fmt.Println("URL:", r.URL) fmt.Println("lastPush:", float64(time.Now().UnixNano())/1e9) fmt.Println("Content-Encoding:", r.Header.Get("Content-Encoding")) fmt.Println("Content-Type:", r.Header.Get("Content-Type")) }) http.HandleFunc("/query", func(w http.ResponseWriter, r *http.Request) { fmt.Println("URL:", r.URL) fmt.Fprintf(w, `{"results": []}`) w.WriteHeader(http.StatusOK) }) http.HandleFunc("/api/v2/query", func(w http.ResponseWriter, r *http.Request) { fmt.Println("URL:", r.URL) fmt.Fprintf(w, ``) w.WriteHeader(http.StatusOK) }) http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) { fmt.Println("URL:", r.URL) http.Error(w, "", http.StatusNoContent) }) metricsPath := "/metrics" //http.Handle("/metrics", promhttp.Handler()) //http.Handle("/metrics/exporter", promhttp.Handler()) http.Handle("/metrics", promhttp.HandlerFor(promRegistry, promhttp.HandlerOpts{})) http.Handle("/metrics/exporter", promhttp.Handler()) http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte(` <html> <head><title>Terminal Exporter Homepage</title></head> <body> <h1>Terminal Exporter</h1> <p><a href="` + metricsPath + `">Metrics</a></p> <p><a href="` + "/metrics/exporter" + `">Exporter Metrics</a></p> </body> </html>`)) }) fmt.Println("terminal", "ListenAndServe", "url:", "http://localhost:9105") if err := http.ListenAndServe(":9105", nil); err != nil { fmt.Println("Error occur when start server:", err) } }