Malheureusement, il peut être coûteux d'externaliser cette responsabilité ; cependant, il existe des solutions open-source, et l'une des meilleures est la plateforme Grafana. Grafana est l'outil de visualisation d'une variété de sources de données, mais l'équipe Grafana a également ses propres sources de données pour les journaux (Loki), les métriques (Mimir) et les traces (Tempo). Dans cet article, nous allons voir comment connecter une application Spring Boot à cet écosystème.
Notre première étape est de mettre en place notre environnement d'observabilité. Nous allons utiliser docker compose pour créer cet environnement avec ce fichier 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
Ici, vous pouvez voir que nous mettons en place une instance Grafana soutenue par un Influxdb ainsi que des instances de Loki, Tempo, et Mimir. Nous exposons les ports appropriés pour configurer chaque instance avec les fichiers correspondants de notre dossier docker. Le plus intéressant est le fichier grafana-datasources.yaml qui configure grafana pour se connecter à Loki, Mimir et 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
Une fois que nous avons lancé "docker compose up", nous pouvons accéder à localhost:9000 et nous connecter à Grafana avec nos identifiants admin/admin1 définis dans le fichier docker compose. Travaillons maintenant à obtenir des données de notre application Spring Boot. Nous allons commencer par ajouter des logs à loki.
Les logs
Par défaut, Spring Boot inclut slf4j, nous n'avons donc pas besoin d'ajouter quoi que ce soit à notre build.gradle. Cependant, dans cet exemple, nous utiliserons Grafana Agent pour récupérer nos logs et les exporter vers Loki. Nous devons donc mettre à jour notre fichier application.properties pour écrire nos logs dans un fichier et définir un niveau de logging root comme ceci :
logging.file.name=logs/app.log
logging.level.root=INFO
Ensuite, nous ajoutons l'agent Grafana à notre fichier 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"
Et nous indiquons à Grafana Agent comment récupérer nos logs en définissant son fichier de configuration (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étriques
Pour envoyer nos métriques, nous devons inclure quelques dépendances dans notre 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'
}
Maintenant, nous allons activer les endpoints de métriques dans notre application en ajoutant quelques propriétés à l'application.properties :
management.endpoint.health.show-details=always management.endpoints.web.exposure.include=health,info,prometheus
Enfin, nous indiquons à Grafana Agent où lire nos métriques et où les écrire en mettant à jour le fichier de configuration grafana-agent.yaml :
management.endpoint.health.show-details=always management.endpoints.web.exposure.include=health,info,prometheus
Notre Spring Boot app acutaor endpoint contient un tas d'excellentes métriques, si vous voulez voir un exemple de métriques personnalisées, vous pouvez regarder le FactorService dans mon application d'exemple.
Traces
Pour ajouter des données de trace à notre application, nous devons inclure quelques dépendances supplémentaires dans notre gradle.build
dependencies {
implementation("io.micrometer:micrometer-tracing")
implementation("io.micrometer:micrometer-tracing-bridge-otel")
implementation("io.opentelemetry:opentelemetry-exporter-zipkin")
}
Maintenant, activons l'ajout de nos informations de trace dans nos logs et activons le traçage et les endpoints dans notre application en ajoutant quelques propriétés à l'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
C'est tout ! Démarrez l'application et relancez votre docker compose pour que nous récupérions tous les changements. Vous pouvez trouver l'ensemble du repo de l'application d'exemple ici https://github.com/scottbock/observability-example