Aller au contenu

Tempo (OpenTelemetry)

Tempo est le puits de traces distribuées de PerfShop. Il reçoit les spans OTLP exportés par l'agent Java OpenTelemetry embarqué dans le backend, les stocke dans des blocs locaux, et alimente les datasources Grafana avec des fonctionnalités riches : corrélation traces ↔ logs, service map, span-metrics dérivées poussées vers Prometheus.

Source de vérité

Cette page est tirée de tempo/tempo-config.yml, des JAVA_OPTS du backend dans les fichiers compose, et de la datasource Tempo dans grafana/provisioning/datasources/tempo.yml.

Version pinned

PerfShop fige Tempo à la version 2.4.2 (image grafana/tempo:2.4.2). C'est la seule image d'observabilité avec Grafana à être pinnée à une version précise (les autres sont en latest). La raison : la configuration metrics_generator et le format des span-metrics ont évolué entre les versions majeures, et l'intégration avec Grafana 12.0.0 a été validée précisément contre Tempo 2.4.2.

Architecture

flowchart LR
  subgraph BE["perfshop-app (JVM)"]
    direction TB
    APP["Spring Boot 3.2"]
    OA["agent OpenTelemetry<br/>opentelemetry-javaagent.jar"]
    APP -.injecté.-> OA
  end

  TEMPO["perfshop-tempo<br/>(2.4.2)"]
  PROM[("Prometheus")]
  GRAF["Grafana"]

  OA -->|"OTLP gRPC :4317<br/>(spans)"| TEMPO
  TEMPO -->|"metrics_generator<br/>service-graphs<br/>span-metrics<br/>remote_write"| PROM
  TEMPO -->|"datasource Tempo<br/>(query)"| GRAF
  PROM -->|"datasource Prometheus<br/>(spanmetrics + serviceMap)"| GRAF

Configuration Tempo

Receivers OTLP

distributor:
  receivers:
    otlp:
      protocols:
        grpc:
          endpoint: 0.0.0.0:4317
        http:
          endpoint: 0.0.0.0:4318

Tempo expose deux endpoints OTLP simultanément :

Port Protocole Usage
4317 gRPC Utilisé par l'agent Java OpenTelemetry de perfshop-app
4318 HTTP Disponible pour des SDK qui ne supportent pas gRPC (curl, Python sans grpcio, etc.)

Les deux ports sont exposés à l'hôte via les variables TEMPO_OTLP_GRPC_PORT et TEMPO_OTLP_HTTP_PORT.

Ingester et stockage

ingester:
  trace_idle_period: 10s
  max_block_bytes: 1_000_000
  max_block_duration: 5m

storage:
  trace:
    backend: local
    block:
      bloom_filter_false_positive: .05
    wal:
      path: /var/tempo/wal
    local:
      path: /var/tempo/blocks
    pool:
      max_workers: 100
      queue_depth: 10000
Paramètre Valeur Effet
trace_idle_period 10s Une trace inactive depuis 10 s est considérée terminée et flushée
max_block_bytes 1 Mo Les blocs WAL sont coupés à 1 Mo
max_block_duration 5m Cap dur de durée par bloc
backend: local Stockage filesystem (volume nommé tempo-data)
wal.path /var/tempo/wal Write-Ahead Log avant flush
local.path /var/tempo/blocks Blocs flushés (immutables, compactés)
bloom_filter_false_positive 0.05 5 % de faux positifs sur les bloom filters d'index

Server

server:
  http_listen_port: 3200
  log_level: warn

stream_over_http_enabled: true
Paramètre Valeur Effet
http_listen_port 3200 API HTTP query (utilisée par Grafana datasource Tempo)
log_level warn Niveau de log Tempo lui-même (pas verbeux)
stream_over_http_enabled true Active le streaming HTTP pour les recherches longues

Le port hôte par défaut est 19200 (variable TEMPO_HTTP_PORT). Le port interne du conteneur reste 3200 — seul le mapping vers le host change.

metrics_generator — l'élément qui change tout

metrics_generator:
  registry:
    external_labels:
      source: tempo
      cluster: perfshop
      job: perfshop-backend
  storage:
    path: /var/tempo/generator/wal
    remote_write:
      - url: http://prometheus:9090/api/v1/write
        send_exemplars: true

overrides:
  defaults:
    metrics_generator:
      processors:
        - service-graphs
        - span-metrics

C'est ici que Tempo devient bien plus qu'un simple stockage de traces. Le metrics_generator analyse les spans en temps réel et génère deux familles de métriques dérivées qu'il pousse dans Prometheus via remote-write :

Famille 1 — service-graphs

Pour chaque paire d'entités (service appelant → service appelé), génère des métriques :

Métrique Type Description
traces_service_graph_request_total counter Nombre d'appels
traces_service_graph_request_failed_total counter Nombre d'appels échoués
traces_service_graph_request_server_seconds_* histogram Latence côté serveur
traces_service_graph_request_client_seconds_* histogram Latence côté client

Ces métriques alimentent le Service Map de Grafana (datasource Tempo → onglet Service Graph) qui affiche un graphe orienté des dépendances entre services.

Famille 2 — span-metrics

Pour chaque opération instrumentée (URI HTTP, méthode, statut), génère :

Métrique Type Description
traces_spanmetrics_calls_total{service, span_name, status_code, ...} counter Nombre d'invocations
traces_spanmetrics_latency_*{service, span_name, ...} histogram Distribution de latence

Ces métriques permettent au panel « Latence P50 / P95 / P99 — toutes routes » du dashboard APM Formateur de fonctionner — il interroge Prometheus, pas Tempo, mais sur des séries dérivées des spans.

Labels externes

external_labels:
  source: tempo
  cluster: perfshop
  job: perfshop-backend

Ces trois labels sont ajoutés à toutes les métriques poussées par Tempo dans Prometheus. Le label job: perfshop-backend aligne les span-metrics avec les autres métriques Spring Boot, ce qui permet de mélanger les deux dans une même requête PromQL.

Exemplaires

remote_write:
  - url: http://prometheus:9090/api/v1/write
    send_exemplars: true

Les exemplaires sont des liens depuis une métrique vers un trace_id spécifique. Quand send_exemplars: true, chaque sample histogram peut transporter un trace_id qui remonte jusqu'à Grafana — l'utilisateur voit alors des points cliquables dans les graphiques de latence, et un clic ouvre la trace correspondante. C'est l'un des trois mécanismes de corrélation métriques ↔ traces de la stack.

Agent OpenTelemetry côté backend

L'agent Java OpenTelemetry est embarqué dans l'image perfshop-backend (chemin /agents/opentelemetry-javaagent.jar) et activé via JAVA_OPTS dans les fichiers compose :

-javaagent:/agents/opentelemetry-javaagent.jar
-Dotel.service.name=perfshop
-Dotel.exporter.otlp.endpoint=http://perfshop-tempo:4317
-Dotel.exporter.otlp.protocol=grpc
-Dotel.traces.exporter=otlp
-Dotel.metrics.exporter=none
-Dotel.logs.exporter=none
-Dotel.instrumentation.http.capture-headers.server.request=X-Admin-Token,Content-Type
-Dotel.instrumentation.jdbc.captured-statements.enabled=true
-Dotel.span.attribute.count.limit=256

Décodage des propriétés

Propriété Effet pédagogique
otel.service.name=perfshop Tous les spans portent service.name=perfshop — apparaît dans les attributs Tempo et permet le filtrage TraceQL {resource.service.name="perfshop"}
otel.exporter.otlp.endpoint=http://perfshop-tempo:4317 Cible l'endpoint gRPC du collector Tempo via DNS Docker
otel.exporter.otlp.protocol=grpc Force gRPC (le défaut bascule selon le port)
otel.traces.exporter=otlp Export OTLP des traces uniquement
otel.metrics.exporter=none Pas d'export OTel des métriques (Prometheus est le puits préféré)
otel.logs.exporter=none Pas d'export OTel des logs (Loki/OpenSearch sont les puits préférés)
otel.instrumentation.http.capture-headers.server.request=X-Admin-Token,Content-Type Capture explicite des headers HTTP côté serveur — c'est ce qui permet le panel formateur « Traces avec déclenchement admin — X-Admin-Token capturé »
otel.instrumentation.jdbc.captured-statements.enabled=true Capture les statements SQL JDBC — affichés dans les détails de span Tempo et utilisés par le panel « Traces SQL instrumentées » du dashboard APM Formateur (utile pour le scénario Security S1 — SQL injection)
otel.span.attribute.count.limit=256 Cap à 256 attributs par span — protège contre l'explosion de cardinalité

Auto-instrumentation

L'agent OpenTelemetry Java auto-instrumente automatiquement plus de 100 frameworks. Pour PerfShop, les plus utiles sont :

Instrumentation Spans générés
Spring Web MVC HTTP GET /api/products, HTTP POST /api/orders, etc.
HikariCP / JDBC SELECT users.*, INSERT orders, avec capture du statement SQL
Hibernate ORM Hibernate Session.flush, etc.
Tomcat HTTP server Spans serveur racine pour chaque requête
java.net.http / OkHttp Spans clients pour les appels HTTP sortants
Logback / SLF4J MDC Injection automatique du trace_id et span_id dans les logs (pour la corrélation Loki)

Aucune ligne de code Spring n'a besoin d'être modifiée pour bénéficier de cette instrumentation — l'agent fait tout au démarrage de la JVM.

Datasource Grafana — corrélations

La datasource Tempo dans grafana/provisioning/datasources/tempo.yml active trois mécanismes de corrélation déjà décrits dans grafana.md :

flowchart LR
  TEMPO["Datasource Tempo"]

  TEMPO -->|tracesToLogsV2| L["Loki<br/>filtre ±1 min<br/>autour du span"]
  TEMPO -->|tracesToMetrics| P["Prometheus<br/>métriques de l'opération"]
  TEMPO -->|serviceMap| SM["Service Map<br/>(via traces_service_graph_*)"]
  TEMPO -->|nodeGraph| NG["Node Graph<br/>(visualisation graphe)"]
  TEMPO -->|lokiSearch| LS["Loki Search<br/>(recherche libre)"]

Le flux pédagogique typique :

  1. Le formateur active un chaos qui provoque des NullPointerException.
  2. Il ouvre le dashboard APM Formateur et regarde le panel « F1 — Traces NullPointerException » (TraceQL {span.exception.type="NullPointerException"}).
  3. Il clique sur une trace pour voir le détail des spans.
  4. Il clique sur un span → tracesToLogsV2 ouvre Loki avec le filtre temporel et le traceID → il voit les logs Spring Boot autour de l'exception.
  5. Optionnellement, il clique sur tracesToMetrics pour voir la latence Prometheus de l'opération sur la même fenêtre.
  6. Pour comprendre le contexte global, il ouvre le Service Map de la datasource Tempo qui affiche le graphe client → perfshop-app → mysql avec les latences moyennes sur chaque arête.

Volumes et persistance

Volume Montage Contenu
tempo-data (volume nommé) /var/tempo WAL, blocs, generator WAL
./tempo/tempo-config.yml (bind mount) /etc/tempo/tempo-config.yml Configuration Tempo (lecture seule)

Ports

Service Port hôte Port container Variable d'env Usage
perfshop-tempo 19200 3200 TEMPO_HTTP_PORT API HTTP query (Grafana datasource)
perfshop-tempo 4317 4317 TEMPO_OTLP_GRPC_PORT OTLP gRPC (export depuis l'agent Java)
perfshop-tempo 4318 4318 TEMPO_OTLP_HTTP_PORT OTLP HTTP (alternative)

Pour aller plus loin

  • Vue d'ensemble — quatre signaux et corrélations
  • Grafana — datasource Tempo et corrélations tracesToLogsV2, tracesToMetrics
  • Dashboards livrés — détail des panels TraceQL du dashboard APM Formateur
  • Prometheus — réception des span-metrics via remote-write
  • Pyroscope — l'autre agent embarqué dans l'image backend