活学活用Prometheus(七):Prometheus监控Go应用实战

Prometheus官方提供了Go语言版本的sdk,Go应用借助sdk可以很方便地接入Prometheus监控,接下来会从几个方面来说明:

创建一个简单的Go应用并暴露一个用于Prometheus监控的HTTP地址

介绍主流的Golang框架接入Prometheus监控的方法,分别是Echo、Gin和gRPC

Go应用需要暴露一个HTTP路由用于Prometheus server拉取监控数据,具体的HTTP路由地址可以自定义,官方建议使用 /metrics 。

一个简单的Go应用例子

package main

import (
        "net/http""github.com/prometheus/client_golang/prometheus/promhttp"
)

funcmain() {
        http.Handle("/metrics", promhttp.Handler())
        http.ListenAndServe(":2112", nil)
}

启动应用后访问 metrics 接口即可看到基础的监控指标

curl http://localhost:2112/metrics

怎么加入自定义的监控指标呢,示例代码如下:

package main

import (
        "net/http""time""github.com/prometheus/client_golang/prometheus""github.com/prometheus/client_golang/prometheus/promauto""github.com/prometheus/client_golang/prometheus/promhttp"
)

funcrecordMetrics() {
        gofunc() {
                for {
                        opsProcessed.Inc()
                        time.Sleep(2 * time.Second)
                }
        }()
}

var (
        opsProcessed = promauto.NewCounter(prometheus.CounterOpts{
                Name: "myapp_processed_ops_total",
                Help: "The total number of processed events",
        })
)

funcmain() {
        recordMetrics()

        http.Handle("/metrics", promhttp.Handler())
        http.ListenAndServe(":2112", nil)
}

启动应用后访问 metrics 接口即可看到添加进去的自定义指标 myapp_processed_ops_total

curl http://localhost:2112/metrics

主流框架接入

1、echo框架接入

监控基础指标

package main
import (
    "github.com/labstack/echo/v4""github.com/labstack/echo-contrib/prometheus"
)
funcmain() {
    e := echo.New()
    // Enable metrics middleware
    p := prometheus.NewPrometheus("echo", nil)
    p.Use(e)

    e.Logger.Fatal(e.Start(":1323"))
}

加入自定义指标

package main

import (
	"time"

	"github.com/labstack/echo-contrib/prometheus"
	"github.com/labstack/echo/v4"
	prom "github.com/prometheus/client_golang/prometheus"
)

const cKeyMetrics = "custom_metrics"// See the NewMetrics func for proper descriptions and prometheus names!// In case you add a metric here later, make sure to include it in the// MetricsList method or you'll going to have a bad time.type Metrics struct {
	customCnt *prometheus.Metric
	customDur *prometheus.Metric
}

// Needed by echo-contrib so echo can register and collect these metricsfunc(m *Metrics)MetricList() []*prometheus.Metric {
	return []*prometheus.Metric{
		// ADD EVERY METRIC HERE!
		m.customCnt,
		m.customDur,
	}
}

// Creates and populates a new Metrics struct// This is where all the prometheus metrics, names and labels are specifiedfuncNewMetrics() *Metrics {
	return &Metrics{
		customCnt: &prometheus.Metric{
			Name:        "custom_total",
			Description: "Custom counter events.",
			Type:        "counter_vec",
			Args:        []string{"label_one", "label_two"},
		},
		customDur: &prometheus.Metric{
			Name:        "custom_duration_seconds",
			Description: "Custom duration observations.",
			Type:        "histogram_vec",
			Args:        []string{"label_one", "label_two"},
			Buckets:     prom.DefBuckets, // or your Buckets
		},
	}
}

// This will push your metrics object into every request context for later usefunc(m *Metrics)AddCustomMetricsMiddleware(next echo.HandlerFunc)echo.HandlerFunc {
	returnfunc(c echo.Context)error {
		c.Set(cKeyMetrics, m)
		return next(c)
	}
}

func(m *Metrics)IncCustomCnt(labelOne, labelTwo string) {
	labels := prom.Labels{"label_one": labelOne, "label_two": labelTwo}
	m.customCnt.MetricCollector.(*prom.CounterVec).With(labels).Inc()
}

func(m *Metrics)ObserveCustomDur(labelOne, labelTwo string, d time.Duration) {
	labels := prom.Labels{"label_one": labelOne, "label_two": labelTwo}
	m.customCnt.MetricCollector.(*prom.HistogramVec).With(labels).Observe(d.Seconds())
}

funcmain() {
	e := echo.New()
	m := NewMetrics()

	// Enable metrics middleware
	p := prometheus.NewPrometheus("echo", nil, m.MetricList())
	p.Use(e)

	e.Use(m.AddCustomMetricsMiddleware)

	e.GET("/custom", func(c echo.Context)error {
		metrics := c.Get(cKeyMetrics).(*Metrics)
		metrics.IncCustomCnt("any", "value")
		returnnil
	})

	e.Logger.Fatal(e.Start(":1323"))
}

2、Ging框架接入

package main

import (
	"github.com/gin-gonic/gin"
	"github.com/sunmi-OS/gocore/v2/lib/prometheus"
	"log"
)

funcmain() {
	r := gin.New()
	//Enable metrics middleware
	p := prometheus.NewPrometheus("app")
	p.Use(r)
	
	log.Fatal(r.Run(":1323"))
}

加入自定义指标

package main

import (
   "github.com/gin-gonic/gin"
   prom "github.com/prometheus/client_golang/prometheus""github.com/sunmi-OS/gocore/v2/lib/prometheus""log""time"
)

const cKeyMetrics = "custom_metrics"// See the NewMetrics func for proper descriptions and prometheus names!// In case you add a metric here later, make sure to include it in the// MetricsList method or you'll going to have a bad time.type Metrics struct {
   customCnt *prometheus.Metric
   customDur *prometheus.Metric
}

// Needed by echo-contrib so echo can register and collect these metricsfunc(m *Metrics)MetricList() []*prometheus.Metric {
   return []*prometheus.Metric{
      // ADD EVERY METRIC HERE!
      m.customCnt,
      m.customDur,
   }
}

// Creates and populates a new Metrics struct// This is where all the prometheus metrics, names and labels are specifiedfuncNewMetrics() *Metrics {
   return &Metrics{
      customCnt: &prometheus.Metric{
         Name:        "custom_total",
         Description: "Custom counter events.",
         Type:        "counter_vec",
         Args:        []string{"label_one", "label_two"},
      },
      customDur: &prometheus.Metric{
         Name:        "custom_duration_seconds",
         Description: "Custom duration observations.",
         Type:        "histogram_vec",
         Args:        []string{"label_one", "label_two"},
      },
   }
}

// This will push your metrics object into every request context for later usefunc(m *Metrics)AddCustomMetricsMiddleware()gin.HandlerFunc {
   returnfunc(c *gin.Context) {
      c.Set(cKeyMetrics, m)
      c.Next()
   }
}

func(m *Metrics)IncCustomCnt(labelOne, labelTwo string) {
   labels := prom.Labels{"label_one": labelOne, "label_two": labelTwo}
   m.customCnt.MetricCollector.(*prom.CounterVec).With(labels).Inc()
}

func(m *Metrics)ObserveCustomDur(labelOne, labelTwo string, d time.Duration) {
   labels := prom.Labels{"label_one": labelOne, "label_two": labelTwo}
   m.customCnt.MetricCollector.(*prom.HistogramVec).With(labels).Observe(d.Seconds())
}

funcmain() {
   r := gin.New()
   m := NewMetrics()

   //Enable metrics middleware
   p := prometheus.NewPrometheus("app", m.MetricList())
   p.Use(r)

   r.GET("/custom", func(c *gin.Context) {
      if v, ok := c.Get(cKeyMetrics); ok {
         metric := v.(*Metrics)
         metric.IncCustomCnt("any", "value")
      }
   })

   log.Fatal(r.Run(":1323"))
}

3、grpc接入

在gRPC服务启动时,使用协程启动一个用于prometheus监控服务的HTTP服务即可。

package main

import (
  ...
	grpcPrometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

funcmain() {
  //集成prometheus监控,使用协程启动一个http服务gofunc() {
		http.Handle("/metrics", promhttp.Handler())
		http.ListenAndServe(":1323", nil)
	}()
  
  //添加prometheus拦截器
  s := grpc.NewServer(
    ...
    grpc.UnaryInterceptor(grpcPrometheus.UnaryServerInterceptor),
    grpc.StreamInterceptor(grpcprometheus.StreamServerInterceptor),
  )
  ...
}

你可能感兴趣的:(活学活用Prometheus,prometheus,golang,开发语言)