构建应用程序
先决条件
- 您有一个 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()
}
}
}在这段代码中,您导入了所需的包 gin、prometheus 和 promhttp。然后您定义了几个变量,HttpRequestTotal 和 HttpRequestErrorTotal 是 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/health 或 https://: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 注册指标。通过实现中间件函数,您可以根据请求路径和状态码增加计数器。
后续步骤
在下一节中,您将学习如何容器化您的应用程序。