構建應用程式

先決條件

  • 您有一個 Git 客戶端。本節中的示例使用基於命令列的 Git 客戶端,但您可以使用任何客戶端。

您將建立一個帶有某些端點的 Golang 伺服器,以模擬真實世界的應用程式。然後,您將使用 Prometheus 從伺服器公開指標。

獲取示例應用程式

克隆示例應用程式以配合本指南使用。開啟終端,將目錄更改到您要工作的目錄,然後執行以下命令克隆儲存庫:

$ git clone https://github.com/dockersamples/go-prometheus-monitoring.git 

克隆後,您將在 go-prometheus-monitoring 目錄中看到以下內容結構:

go-prometheus-monitoring
├── CONTRIBUTING.md
├── Docker
│   ├── grafana.yml
│   └── prometheus.yml
├── dashboard.json
├── Dockerfile
├── LICENSE
├── README.md
├── compose.yaml
├── go.mod
├── go.sum
└── main.go
  • main.go - 應用程式的入口點。
  • go.mod 和 go.sum - Go 模組檔案。
  • Dockerfile - 用於構建應用程式的 Dockerfile。
  • Docker/ - 包含 Grafana 和 Prometheus 的 Docker Compose 配置檔案。
  • compose.yaml - 用於啟動所有內容(Golang 應用程式、Prometheus 和 Grafana)的 Compose 檔案。
  • dashboard.json - Grafana 儀表板配置檔案。
  • Dockerfile - 用於構建 Golang 應用程式的 Dockerfile。
  • compose.yaml - 用於啟動所有內容(Golang 應用程式、Prometheus 和 Grafana)的 Docker Compose 檔案。
  • 其他檔案用於許可和文件目的。

瞭解應用程式

以下是您將在 main.go 中找到的應用程式的完整邏輯。

package main

import (
	"strconv"

	"github.com/gin-gonic/gin"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

// Define metrics
var (
	HttpRequestTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
		Name: "api_http_request_total",
		Help: "Total number of requests processed by the API",
	}, []string{"path", "status"})

	HttpRequestErrorTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
		Name: "api_http_request_error_total",
		Help: "Total number of errors returned by the API",
	}, []string{"path", "status"})
)

// Custom registry (without default Go metrics)
var customRegistry = prometheus.NewRegistry()

// Register metrics with custom registry
func init() {
	customRegistry.MustRegister(HttpRequestTotal, HttpRequestErrorTotal)
}

func main() {
	router := gin.Default()

	// Register /metrics before middleware
	router.GET("/metrics", PrometheusHandler())
	
	router.Use(RequestMetricsMiddleware())
	router.GET("/health", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "Up and running!",
		})
	})
	router.GET("/v1/users", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "Hello from /v1/users",
		})
	})

	router.Run(":8000")
}

// Custom metrics handler with custom registry
func PrometheusHandler() gin.HandlerFunc {
	h := promhttp.HandlerFor(customRegistry, promhttp.HandlerOpts{})
	return func(c *gin.Context) {
		h.ServeHTTP(c.Writer, c.Request)
	}
}

// Middleware to record incoming requests metrics
func RequestMetricsMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		path := c.Request.URL.Path
		c.Next()
		status := c.Writer.Status()
		if status < 400 {
			HttpRequestTotal.WithLabelValues(path, strconv.Itoa(status)).Inc()
		} else {
			HttpRequestErrorTotal.WithLabelValues(path, strconv.Itoa(status)).Inc()
		}
	}
}

在這段程式碼中,您匯入了所需的包 ginprometheuspromhttp。然後您定義了幾個變數,HttpRequestTotalHttpRequestErrorTotal 是 Prometheus 計數器指標,customRegistry 是一個自定義登錄檔,將用於註冊這些指標。指標名稱是一個字串,您可以使用它來識別指標。幫助字串是當您查詢 /metrics 端點以瞭解指標時將顯示的字串。您使用自定義登錄檔的原因是為了避免 Prometheus 客戶端預設註冊的預設 Go 指標。然後,使用 init 函式,您將指標註冊到自定義登錄檔。

import (
	"strconv"

	"github.com/gin-gonic/gin"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

// Define metrics
var (
	HttpRequestTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
		Name: "api_http_request_total",
		Help: "Total number of requests processed by the API",
	}, []string{"path", "status"})

	HttpRequestErrorTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
		Name: "api_http_request_error_total",
		Help: "Total number of errors returned by the API",
	}, []string{"path", "status"})
)

// Custom registry (without default Go metrics)
var customRegistry = prometheus.NewRegistry()

// Register metrics with custom registry
func init() {
	customRegistry.MustRegister(HttpRequestTotal, HttpRequestErrorTotal)
}

main 函式中,您建立了一個新的 gin 框架例項並建立了三個路由。您可以看到路徑為 /health 的健康端點,它將返回一個帶有 {"message": "Up and running!"} 的 JSON,以及路徑為 /v1/users 的端點,它將返回一個帶有 {"message": "Hello from /v1/users"} 的 JSON。第三個路由是用於 /metrics 端點,它將以 Prometheus 格式返回指標。然後您有 RequestMetricsMiddleware 中介軟體,它將針對對 API 的每個請求呼叫。它將記錄傳入請求的指標,例如狀態碼和路徑。最後,您在埠 8000 上執行 gin 應用程式。

func main() {
	router := gin.Default()

	// Register /metrics before middleware
	router.GET("/metrics", PrometheusHandler())
	
	router.Use(RequestMetricsMiddleware())
	router.GET("/health", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "Up and running!",
		})
	})
	router.GET("/v1/users", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "Hello from /v1/users",
		})
	})

	router.Run(":8000")
}

現在是中介軟體函式 RequestMetricsMiddleware。此函式針對對 API 的每個請求呼叫。如果狀態碼小於或等於 400,它會增加 HttpRequestTotal 計數器(不同路徑和狀態碼有不同的計數器)。如果狀態碼大於 400,它會增加 HttpRequestErrorTotal 計數器(不同路徑和狀態碼有不同的計數器)。PrometheusHandler 函式是自定義處理程式,將針對 /metrics 端點呼叫。它將以 Prometheus 格式返回指標。

// Custom metrics handler with custom registry
func PrometheusHandler() gin.HandlerFunc {
	h := promhttp.HandlerFor(customRegistry, promhttp.HandlerOpts{})
	return func(c *gin.Context) {
		h.ServeHTTP(c.Writer, c.Request)
	}
}

// Middleware to record incoming requests metrics
func RequestMetricsMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		path := c.Request.URL.Path
		c.Next()
		status := c.Writer.Status()
		if status < 400 {
			HttpRequestTotal.WithLabelValues(path, strconv.Itoa(status)).Inc()
		} else {
			HttpRequestErrorTotal.WithLabelValues(path, strconv.Itoa(status)).Inc()
		}
	}
}

就是這樣,這是應用程式的完整要點。現在是時候執行並測試應用程式是否正確註冊指標了。

執行應用程式

確保您仍在終端的 go-prometheus-monitoring 目錄中,並執行以下命令。透過執行 go mod tidy 安裝依賴項,然後透過執行 go run main.go 構建並執行應用程式。然後訪問 https://:8000/healthhttps://:8000/v1/users。您應該看到輸出 {"message": "Up and running!"}{"message": "Hello from /v1/users"}。如果您能看到這些,那麼您的應用程式已成功啟動並執行。

現在,透過訪問 /metrics 端點檢查應用程式的指標。在瀏覽器中開啟 https://:8000/metrics。您應該看到類似以下的輸出。

# HELP api_http_request_error_total Total number of errors returned by the API
# TYPE api_http_request_error_total counter
api_http_request_error_total{path="/",status="404"} 1
api_http_request_error_total{path="//v1/users",status="404"} 1
api_http_request_error_total{path="/favicon.ico",status="404"} 1
# HELP api_http_request_total Total number of requests processed by the API
# TYPE api_http_request_total counter
api_http_request_total{path="/health",status="200"} 2
api_http_request_total{path="/v1/users",status="200"} 1

在終端中,按 ctrl + c 停止應用程式。

注意

如果您不想在本地執行應用程式,並希望在 Docker 容器中執行它,請跳到下一頁,其中您將建立一個 Dockerfile 並容器化應用程式。

摘要

在本節中,您學習瞭如何建立 Golang 應用程式以使用 Prometheus 註冊指標。透過實現中介軟體函式,您可以根據請求路徑和狀態碼增加計數器。

後續步驟

在下一節中,您將學習如何容器化您的應用程式。