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





