构建应用程序

先决条件

  • 您有一个 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 注册指标。通过实现中间件函数,您可以根据请求路径和状态码增加计数器。

后续步骤

在下一节中,您将学习如何容器化您的应用程序。

© . This site is unofficial and not affiliated with Kubernetes or Docker Inc.