概述

在本文中,我们将会以 Go 项目为例,演示如何使用 Prometheus 提供的埋点库来实现 Prometheus 的埋点操作。

QuickStart

Step1: 安装 client 库

  1. go get github.com/prometheus/client_golang/prometheus
  2. go get github.com/prometheus/client_golang/prometheus/promauto
  3. go get github.com/prometheus/client_golang/prometheus/promhttp

Step2: 编写示例程序

为了在 Go 应用中提供 Prometheus 的监控项,你需要提供一个 /metrics 的 HTTP endpoint。你可以使用 prometheus/promhttp 库中的 Handler 来作为函数处理逻辑。
一个示例代码如下:

  1. package main
  2. import (
  3. "net/http"
  4. "github.com/prometheus/client_golang/prometheus/promhttp"
  5. )
  6. func main() {
  7. http.Handle("/metrics", promhttp.Handler())
  8. http.ListenAndServe(":2112", nil)
  9. }

此时,你可以访问 http://localhost:2112/metrics 来查询对应的监控指标。

Step3: 添加自己的 metrics 指标

上述示例中,仅仅提供了一些默认的 metrics 指标。此外,你还可以注册自己自定义的应用特定的相关指标。例如,在如下应用中,我们提供了一个 myapp_processed_ops_total 的 Counter 类型的指标用于统计程序操作的次数。在如下程序中,每隔 2s 后,对应的 counter 指标会自增 1。

  1. package main
  2. import (
  3. "net/http"
  4. "time"
  5. "github.com/prometheus/client_golang/prometheus"
  6. "github.com/prometheus/client_golang/prometheus/promauto"
  7. "github.com/prometheus/client_golang/prometheus/promhttp"
  8. )
  9. func recordMetrics() {
  10. // 每隔 2s 自增 1
  11. go func() {
  12. for {
  13. opsProcessed.Inc()
  14. time.Sleep(2 * time.Second)
  15. }
  16. }()
  17. }
  18. var (
  19. // 定义了一个 myapp_processed_ops_total 指标,Counter 类型
  20. opsProcessed = promauto.NewCounter(prometheus.CounterOpts{
  21. Name: "myapp_processed_ops_total",
  22. Help: "The total number of processed events",
  23. })
  24. )
  25. func main() {
  26. recordMetrics()
  27. http.Handle("/metrics", promhttp.Handler())
  28. http.ListenAndServe(":2112", nil)
  29. }

go-gin-prometheus 快速上手

gin 是 Go 语言中流行的一个 Web 框架。下面,我们来讲解一下针对 Gin 框架而言,如果实现 Prometheus 的埋点操作。

Step1: 安装 client 库

  1. go get github.com/zsais/go-gin-prometheus

Step2: QuickStart

  1. package main
  2. import (
  3. "github.com/gin-gonic/gin"
  4. "github.com/zsais/go-gin-prometheus"
  5. )
  6. func main() {
  7. r := gin.New()
  8. p := ginprometheus.NewPrometheus("gin")
  9. p.Use(r)
  10. r.GET("/", func(c *gin.Context) {
  11. c.JSON(200, "Hello world!")
  12. })
  13. r.Run(":29090")
  14. }

默认情况下,go-gin-prometheus 将会安装每个请求 url 作为 label 统计对应的请求次数,并作为 gin_requests_total 指标对外暴露。
但是这样会有一个问题,如果你的 url 中包含类似这样 /customer/:name 的 url 匹配规则,就会导致 labels 爆炸。一方面会使得你的指标统计规则变得更加复杂,同时也会严重的影响 Prometheus 的性能。
因此,你还需要编写一个 mapping 函数来传递给 middleware 用于重写 Labels 的计算规则:

  1. package main
  2. import (
  3. "github.com/gin-gonic/gin"
  4. "github.com/zsais/go-gin-prometheus"
  5. )
  6. func main() {
  7. r := gin.New()
  8. p := ginprometheus.NewPrometheus("gin")
  9. p.ReqCntURLLabelMappingFn = func(c *gin.Context) string {
  10. url := c.Request.URL.Path
  11. for _, p := range c.Params {
  12. if p.Key == "name" {
  13. url = strings.Replace(url, p.Value, ":name", 1)
  14. break
  15. }
  16. }
  17. return url
  18. }
  19. p.Use(r)
  20. r.GET("/", func(c *gin.Context) {
  21. c.JSON(200, "Hello world!")
  22. })
  23. r.Run(":29090")
  24. }

这样一来,它就可以将 url_pattern 作为 label 来生成对应的指标数据。