Compartir comentarios
Las respuestas se generan en base a la documentación.

Creación de la aplicación

Requisitos previos

  • Tienes un cliente de Git. Los ejemplos de esta sección utilizan un cliente de Git basado en la línea de comandos, pero puedes utilizar cualquier cliente.

Crearás un servidor Golang con algunos endpoints para simular una aplicación del mundo real. Luego, expondrás métricas desde el servidor usando Prometheus.

Obtención de la aplicación de ejemplo

Clona la aplicación de ejemplo para usarla con esta guía. Abre una terminal, cambia al directorio en el que quieras trabajar y ejecuta el siguiente comando para clonar el repositorio:

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

Una vez clonado, verás la siguiente estructura de contenido dentro del directorio 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 - El punto de entrada de la aplicación.
  • go.mod y go.sum - Archivos de módulos de Go.
  • Dockerfile - El Dockerfile utilizado para construir la aplicación.
  • Docker/ - Contiene los archivos de configuración de Docker Compose para Grafana y Prometheus.
  • compose.yaml - El archivo Compose para lanzar todo (la aplicación Golang, Prometheus y Grafana).
  • dashboard.json - Archivo de configuración del tablero de Grafana.
  • Dockerfile - El Dockerfile utilizado para construir la aplicación Golang.
  • compose.yaml - El archivo Docker Compose para lanzar todo (la aplicación Golang, Prometheus y Grafana).
  • Otros archivos son para fines de licencia y documentación.

Entendiendo la aplicación

La siguiente es la lógica completa de la aplicación que encontrarás en main.go.

package main

import (
	"strconv"

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

// Definir métricas
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"})
)

// Registro personalizado (sin las métricas predeterminadas de Go)
var customRegistry = prometheus.NewRegistry()

// Registrar métricas con el registro personalizado
func init() {
	customRegistry.MustRegister(HttpRequestTotal, HttpRequestErrorTotal)
}

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

	// Registrar /metrics antes del 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")
}

// Manejador de métricas personalizado con registro personalizado
func PrometheusHandler() gin.HandlerFunc {
	h := promhttp.HandlerFor(customRegistry, promhttp.HandlerOpts{})
	return func(c *gin.Context) {
		h.ServeHTTP(c.Writer, c.Request)
	}
}

// Middleware para registrar las métricas de las solicitudes entrantes
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()
		}
	}
}

En esta parte del código, has importado los paquetes requeridos gin, prometheus y promhttp. Luego has definido un par de variables, HttpRequestTotal y HttpRequestErrorTotal que son métricas de tipo contador de Prometheus, y customRegistry que es un registro personalizado que se utilizará para registrar estas métricas. El nombre de la métrica es una cadena que puedes usar para identificarla. La cadena de ayuda (help string) es un texto que se mostrará cuando realices consultas al endpoint /metrics para comprender la métrica. La razón por la que utilizas el registro personalizado es para evitar las métricas predeterminadas de Go que el cliente de Prometheus registra de forma predeterminada. Luego, mediante la función init, registras las métricas en el registro personalizado.

import (
	"strconv"

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

// Definir métricas
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"})
)

// Registro personalizado (sin las métricas predeterminadas de Go)
var customRegistry = prometheus.NewRegistry()

// Registrar métricas con el registro personalizado
func init() {
	customRegistry.MustRegister(HttpRequestTotal, HttpRequestErrorTotal)
}

En la función main, has creado una nueva instancia del framework gin y has configurado tres rutas. Puedes ver el endpoint de salud en la ruta /health que devolverá un JSON con {"message": "Up and running!"} y el endpoint /v1/users que devolverá un JSON con {"message": "Hello from /v1/users"}. La tercera ruta es para el endpoint /metrics que devolverá las métricas en el formato de Prometheus. También tienes el middleware RequestMetricsMiddleware, el cual se ejecutará para cada solicitud realizada a la API. Este registrará las métricas de las solicitudes entrantes, como los códigos de estado y las rutas. Finalmente, ejecutas la aplicación gin en el puerto 8000.

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

	// Registrar /metrics antes del 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")
}

Ahora viene la función middleware RequestMetricsMiddleware. Esta función se ejecuta para cada solicitud realizada a la API. Incrementa el contador HttpRequestTotal (un contador diferente para cada ruta y código de estado) si el código de estado es menor que 400. Si el código de estado es mayor o igual a 400, incrementa el contador HttpRequestErrorTotal (un contador diferente para cada ruta y código de estado). La función PrometheusHandler es el manejador personalizado que se llamará para el endpoint /metrics. Devolverá las métricas en el formato de Prometheus.

// Manejador de métricas personalizado con registro personalizado
func PrometheusHandler() gin.HandlerFunc {
	h := promhttp.HandlerFor(customRegistry, promhttp.HandlerOpts{})
	return func(c *gin.Context) {
		h.ServeHTTP(c.Writer, c.Request)
	}
}

// Middleware para registrar las métricas de las solicitudes entrantes
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()
		}
	}
}

Eso es todo, esta es la esencia completa de la aplicación. Ahora es momento de ejecutarla y probar si la aplicación está registrando las métricas correctamente.

Ejecución de la aplicación

Asegúrate de seguir dentro del directorio go-prometheus-monitoring en la terminal y ejecuta el siguiente comando. Instala las dependencias ejecutando go mod tidy y luego compila y ejecuta la aplicación con go run main.go. Después, visita http://localhost:8000/health o http://localhost:8000/v1/users. Deberías ver la salida {"message": "Up and running!"} o {"message": "Hello from /v1/users"}. Si puedes ver esto, tu aplicación está en funcionamiento con éxito.

Ahora, verifica las métricas de tu aplicación accediendo al endpoint /metrics. Abre http://localhost:8000/metrics en tu navegador. Deberías ver una salida similar a la siguiente.

# 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

En la terminal, presiona ctrl + c para detener la aplicación.

Note

Si no quieres ejecutar la aplicación localmente y prefieres ejecutarla en un contenedor Docker, salta a la siguiente página donde crearás un Dockerfile y contenedorizarás la aplicación.

Resumen

En esta sección, aprendiste a crear una aplicación Golang para registrar métricas con Prometheus. Al implementar funciones middleware, pudiste incrementar los contadores según la ruta de la solicitud y los códigos de estado.

Próximos pasos

En la siguiente sección, aprenderás a contenedorizar tu aplicación.