Background Image
TECNOLOGÍA

Añadir una aplicación Spring Boot a la plataforma de observabilidad Grafana

Headshot - Scott Bock
Scott Bock
Vicepresidente de Consultoría

May 9, 2023 | 5 Minuto(s) de lectura

Hay muchos vectores para que aumente la complejidad de una API. Por ejemplo: A medida que añade capacidades, puede convertirse en un monolito complejo o en un conjunto de microservicios. A medida que crece su uso, puede empujar las capacidades del hardware, o convertirse en distribuida. Estos son solo algunos de los problemas y posibles soluciones. Cada una de estas opciones o cambios dificulta el seguimiento de la salud, la depuración de errores y la identificación de cuellos de botella. Por eso es importante la observabilidad.

Por desgracia, puede resultar caro externalizar esta responsabilidad; sin embargo, existen soluciones de código abierto, y una de las mejores es la plataforma Grafana. Grafana es la herramienta de visualización para una variedad de fuentes de datos, pero el equipo de Grafana también tiene sus propias fuentes de datos para registros (Loki), métricas (Mimir) y trazas (Tempo). En este artículo veremos cómo conectar una aplicación Spring Boot a este ecosistema.

Nuestro primer paso es configurar nuestro entorno de observabilidad. Utilizaremos docker compose para crear este entorno con este archivo docker-compose.yaml:

version: "3.9"

networks:
telemetry:

volumes:
influxdb-storage:
grafana-storage:

services:

grafana:
image: grafana/grafana:9.3.1
depends_on:
- influxdb
volumes:
- ./docker/grafana-datasources.yaml:/etc/grafana/provisioning/datasources/datasources.yaml
- grafana-storage:/var/lib/grafana
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=admin1
- GF_SERVER_HTTP_PORT=3000
- INFLUXDB_HOST=influxdb
- INFLUXDB_PORT=8086
- INFLUXDB_NAME=db0
- INFLUXDB_USER=influxuser
- INFLUXDB_PASS=influxuser1
ports:
- "3000:3000"
networks:
- telemetry

influxdb:
image: influxdb:latest
ports:
- '8086:8086'
volumes:
- influxdb-storage:/var/lib/influxdb
environment:
- INFLUXDB_URL=http://influxdb:8086
- INFLUXDB_ADMIN_USER=influxuser
- INFLUXDB_ADMIN_PASSWORD=influxuser1

loki:
image: grafana/loki:2.7.1
ports:
- "3100:3100"
command: -config.file=/etc/loki/loki.yaml
volumes:
- ./docker/loki.yaml:/etc/loki/loki.yaml
networks:
- telemetry

tempo:
image: grafana/tempo:1.5.0
command: [ "-config.file=/etc/tempo.yaml" ]
volumes:
- ./docker/tempo.yaml:/etc/tempo.yaml
- ./data/tempo:/tmp/tempo
ports:
- "14268:14268" # jaeger ingest
- "3200:3200" # tempo
- "55680:55680" # otlp grpc
- "55681:55681" # otlp http
- "9411:9411" # zipkin
- "4318:4318" # new http
- "4317:4317" # new grpc

networks:
- telemetry

mimir:
image: grafana/mimir:2.5.0
command: "-config.file=/etc/mimir/mimir.yaml"
ports:
- "9009:9009"
volumes:
- "./docker/mimir.yaml:/etc/mimir/mimir.yaml"
- "/tmp/mimir/rules:/tmp/mimir/rules"
networks:
- telemetry

Aquí puedes ver que estamos creando una instancia de Grafana respaldada por una Influxdb junto con instancias de Loki, Tempo y Mimir. Exponemos los puertos apropiados para configurar cada instancia con los archivos correspondientes de nuestra carpeta docker. Lo más interesante es el grafana-datasources.yaml que dice grafana configura para conectarse a Loki, Mimir, y Tempo.

apiVersion: 1

datasources:

- name: Tempo
type: tempo
access: proxy
orgId: 1
url: http://tempo:3200
basicAuth: false
isDefault: true
version: 1
editable: false
apiVersion: 1
uid: tempo

- name: Loki
type: loki
access: proxy
orgId: 1
url: http://loki:3100
basicAuth: false
isDefault: false
version: 1
editable: false
apiVersion: 1
jsonData:
derivedFields:
- datasourceUid: tempo
matcherRegex: (?:traceID|trace_id)=(\w+)
name: TraceID
url: $${__value.raw}

- name: Mimir
type: prometheus
access: proxy
orgId: 1
url: http://mimir:9009/prometheus
isDefault: false
version: 1
editable: true

Una vez que ejecutamos "docker compose up" podemos acceder a localhost:9000 e iniciar sesión en Grafana con nuestras credenciales admin/admin1 definidas en el archivo docker compose. Ahora vamos a trabajar en conseguir algunos datos de nuestra aplicación Spring Boot. Empezaremos añadiendo logs a loki.

Registros

Por defecto, Spring Boot incluye slf4j por lo que no necesitamos añadir nada a nuestro build.gradle. Sin embargo, en este ejemplo, utilizaremos Grafana Agent para extraer nuestros logs y exportarlos a Loki, por lo que necesitamos actualizar nuestro archivo application.properties para escribir nuestros logs en un archivo y establecer un nivel de logging raíz como este:

logging.file.name=logs/app.log
logging.level.root=INFO

A continuación añadimos el Grafana Agent a nuestro archivo docker-compose.yaml:

grafana-agent:
image: grafana/agent:v0.22.0
volumes:
- ./docker/grafana-agent.yaml:/etc/agent-config/grafana-agent.yaml
- ./logs/:/var/log/
entrypoint:
- /bin/agent
- -config.file=/etc/agent-config/grafana-agent.yaml
- -prometheus.wal-directory=/tmp/agent/wal
ports:
- "12345:12345"
networks:
- telemetry
extra_hosts:
- "host.docker.internal:host-gateway"

Y le decimos a Grafana Agent cómo raspar nuestros logs configurando su archivo de configuración (grafana-agent.yaml).

server:
 log_level: debug
 http_listen_port: 12345
 
logs:
 configs:
 - name: default
 positions:
 filename: /tmp/localhost-positions.yaml
 clients:
 - url: http://loki:3100/loki/api/v1/push
 scrape_configs:
- job_name: system
 static_configs:
- labels:
 job: localhostlogs 
__path__: /var/log/*log
 env: "local"
 app: "observability-example"

Métricas

Para enviar nuestras métricas necesitamos incluir algunas dependencias en nuestro gradle.build

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-actuator'

//enable /actuator/prometheus
runtimeOnly 'io.micrometer:micrometer-registry-prometheus'

//for timed aspect
implementation 'org.springframework:spring-aspects'
}

Ahora vamos a habilitar los endpoints de métricas en nuestra aplicación añadiendo algunas propiedades al application.properties:

management.endpoint.health.show-details=always management.endpoints.web.exposure.include=health,info,prometheus

Finalmente, le decimos a Grafana Agent de dónde leer nuestras métricas y dónde escribirlas actualizando el archivo de configuración grafana-agent.yaml:

management.endpoint.health.show-details=always management.endpoints.web.exposure.include=health,info,prometheus

Nuestro endpoint acutaor de la app Spring Boot contiene un montón de métricas geniales, si quieres ver ejemplos de algunas métricas personalizadas puedes mirar el FactorService en mi aplicación de ejemplo.

Trazas

Para añadir datos de trazas a nuestra aplicación necesitamos incluir algunas dependencias más en nuestro gradle.build

dependencies {
implementation("io.micrometer:micrometer-tracing")
implementation("io.micrometer:micrometer-tracing-bridge-otel")
implementation("io.opentelemetry:opentelemetry-exporter-zipkin")
}

Ahora vamos a habilitar la información de rastreo en nuestros logs y habilitar el rastreo y los endpoints en nuestra aplicación añadiendo algunas propiedades al application.properties:

logging.pattern.level="trace_id=%mdc{traceId} span_id=%mdc{spanId} trace_flags=%mdc{traceFlags} %p"

management.tracing.enabled=true
management.tracing.sampling.probability=1.0
management.zipkin.tracing.endpoint=http://localhost:9411

¡Ya está! Inicia la aplicación y relanza tu docker compose para que podamos recoger todos los cambios. Puedes encontrar todo el repositorio de la aplicación de ejemplo aquí https://github.com/scottbock/observability-example

Tecnología
Ingeniería de plataformas

¿Necesita ayuda para crear su próxima aplicación Go?

Reflexiones más recientes

Explore las entradas de nuestro blog e inspírese con los líderes de opinión de todas nuestras empresas.
Asset - Unlock the Value Thumbnail
Nube

Transformación de la planificación financiera: De SAP BPC a SAP Analytics Cloud (SAC)

Explore las ventajas y los retos de la transición de SAP BPC a SAP Analytics Cloud (SAC) para la planificación financiera.