Traces, Métriques, Logs : le trio de l’observabilité
L’observabilité, c’est la capacité à comprendre l’état interne de ton système en observant ses sorties. En 2026, ça repose sur trois piliers :
Métriques
Des valeurs numériques agrégées dans le temps. Elles répondent à “combien ?” et “à quelle vitesse ?”.
http_requests_total{method="GET", status="200"} → 142857
http_request_duration_seconds{quantile="0.99"} → 0.250
system_cpu_usage → 0.45
Caractéristiques :
- Faible coût de stockage (agrégées)
- Idéales pour les alertes
- Dashboards et tendances
- Ne donnent pas le “pourquoi”
Logs
Des événements textuels horodatés. Ils racontent ce qui s’est passé.
{
"timestamp": "2026-03-20T08:15:30Z",
"level": "ERROR",
"service": "payment-api",
"message": "Failed to process payment",
"trace_id": "abc123def456",
"user_id": "usr_789",
"error": "timeout connecting to stripe API"
}
Caractéristiques :
- Détails riches sur les événements
- Coût de stockage élevé (texte brut)
- Difficile à corréler sans trace_id
- Debug et post-mortem
Traces (distributed tracing)
Le parcours complet d’une requête à travers tous les services. C’est le chaînon qui lie les métriques et les logs.
[Trace: abc123def456]
├── [Span] api-gateway (12ms)
│ ├── [Span] auth-service.validate (3ms)
│ └── [Span] payment-service.process (8ms)
│ ├── [Span] db.query (2ms)
│ └── [Span] stripe-api.charge (5ms) ← ERREUR ICI
Caractéristiques :
- Visibilité end-to-end sur les requêtes
- Identifie les goulots d’étranglement
- Corrèle les services entre eux
- Essentiel en architecture microservices
Le trio ensemble
Alerte (métrique) → "Le taux d'erreur 5xx dépasse 5%"
↓
Trace → "Les erreurs viennent du payment-service → stripe-api"
↓
Logs → "timeout connecting to stripe API, retry 3/3 failed"
↓
Action → "Stripe a un incident, activer le circuit breaker"
Sans les trois, tu navigues à l’aveugle. Les métriques te disent quoi, les traces te disent où, les logs te disent pourquoi.
OpenTelemetry vs solutions propriétaires
C’est quoi OpenTelemetry ?
OpenTelemetry (OTel) est un framework d’observabilité open source qui fournit :
- Des APIs et SDKs pour instrumenter ton code
- Un Collector pour recevoir, traiter et exporter les données
- Un protocole standard (OTLP) pour transporter les données
C’est un projet CNCF — le 2ème plus actif après Kubernetes.
Pourquoi pas Datadog/New Relic/Dynatrace directement ?
| Aspect | Solution propriétaire | OpenTelemetry |
|---|---|---|
| Vendor lock-in | 🔴 Fort | 🟢 Aucun |
| Coût | 💰💰💰 ($15-27/host/mois) | 🟢 Gratuit (infra à gérer) |
| Setup initial | 🟢 Rapide (agent = done) | 🟡 Plus de config |
| Flexibilité | 🟡 Limitée à leurs features | 🟢 Totale |
| Standards | 🔴 Propriétaire | 🟢 OTLP standard |
| Backend | 🔴 Leur cloud uniquement | 🟢 N’importe quel backend |
| Communauté | 🟡 Forums vendeur | 🟢 Open source massive |
Le compromis intelligent
Tu peux utiliser OpenTelemetry pour l’instrumentation et exporter vers n’importe quel backend :
App → OTel SDK → OTel Collector → Datadog (ou Grafana, ou Jaeger, ou...)
Si tu changes de backend demain, tu changes juste la config de l’exporteur. Pas une ligne de code à toucher.
L’écosystème OTel en 2026
| Composant | Status | Description |
|---|---|---|
| Traces | ✅ Stable | GA depuis 2023 |
| Métriques | ✅ Stable | GA depuis 2023 |
| Logs | ✅ Stable | GA en 2024 |
| Profiling | 🟡 Beta | Continu profiling |
| OTel Collector | ✅ Stable | Le couteau suisse |
| Auto-instrumentation | ✅ Stable | Java, Python, Node.js, .NET, Go |
Instrumentation : auto vs manuelle
Auto-instrumentation
L’auto-instrumentation intercepte automatiquement les appels HTTP, DB, gRPC, etc. Zéro code à écrire.
Node.js :
npm install @opentelemetry/auto-instrumentations-node @opentelemetry/sdk-node
// tracing.js — à charger AVANT ton app
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http');
const { OTLPMetricExporter } = require('@opentelemetry/exporter-metrics-otlp-http');
const { PeriodicExportingMetricReader } = require('@opentelemetry/sdk-metrics');
const sdk = new NodeSDK({
serviceName: 'payment-api',
traceExporter: new OTLPTraceExporter({
url: 'http://otel-collector:4318/v1/traces',
}),
metricReader: new PeriodicExportingMetricReader({
exporter: new OTLPMetricExporter({
url: 'http://otel-collector:4318/v1/metrics',
}),
exportIntervalMillis: 15000,
}),
instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();
console.log('OpenTelemetry initialized');
process.on('SIGTERM', () => {
sdk.shutdown().then(() => process.exit(0));
});
# Lancer avec l'instrumentation
node --require ./tracing.js server.js
Python :
pip install opentelemetry-distro opentelemetry-exporter-otlp
opentelemetry-bootstrap -a install # Installe les instrumentations détectées
# Lancer avec auto-instrumentation
opentelemetry-instrument \
--service_name payment-api \
--traces_exporter otlp \
--metrics_exporter otlp \
--exporter_otlp_endpoint http://otel-collector:4317 \
python app.py
Java (agent JAR) :
# Télécharger l'agent
curl -LO https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar
# Lancer avec l'agent
java -javaagent:opentelemetry-javaagent.jar \
-Dotel.service.name=payment-api \
-Dotel.exporter.otlp.endpoint=http://otel-collector:4317 \
-jar app.jar
Kubernetes — L’opérateur OTel peut injecter l’auto-instrumentation automatiquement :
apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata:
name: auto-instrumentation
namespace: production
spec:
exporter:
endpoint: http://otel-collector.observability:4317
propagators:
- tracecontext
- baggage
sampler:
type: parentbased_traceidratio
argument: "0.25" # Échantillonner 25% des traces
nodejs:
image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-nodejs:latest
python:
image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-python:latest
java:
image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-java:latest
# Annoter le deployment pour activer l'auto-instrumentation
apiVersion: apps/v1
kind: Deployment
metadata:
name: payment-api
spec:
template:
metadata:
annotations:
instrumentation.opentelemetry.io/inject-nodejs: "true" # ou inject-python, inject-java
spec:
containers:
- name: payment-api
image: ghcr.io/devopslab/payment-api:latest
Instrumentation manuelle
Pour des spans et métriques custom, il faut instrumenter manuellement.
Node.js :
const { trace, metrics, SpanStatusCode } = require('@opentelemetry/api');
// Créer un tracer
const tracer = trace.getTracer('payment-service');
// Créer un meter
const meter = metrics.getMeter('payment-service');
// Métriques custom
const paymentCounter = meter.createCounter('payments_processed_total', {
description: 'Total number of payments processed',
});
const paymentDuration = meter.createHistogram('payment_duration_ms', {
description: 'Payment processing duration in milliseconds',
});
async function processPayment(userId, amount) {
// Créer un span
return tracer.startActiveSpan('process-payment', async (span) => {
const startTime = Date.now();
try {
// Ajouter des attributs au span
span.setAttribute('user.id', userId);
span.setAttribute('payment.amount', amount);
span.setAttribute('payment.currency', 'CHF');
// Span enfant pour l'appel DB
const result = await tracer.startActiveSpan('db.query', async (dbSpan) => {
dbSpan.setAttribute('db.system', 'postgresql');
dbSpan.setAttribute('db.statement', 'INSERT INTO payments...');
const res = await db.insertPayment(userId, amount);
dbSpan.end();
return res;
});
// Span enfant pour l'appel Stripe
await tracer.startActiveSpan('stripe.charge', async (stripeSpan) => {
stripeSpan.setAttribute('http.method', 'POST');
stripeSpan.setAttribute('http.url', 'https://api.stripe.com/v1/charges');
await stripe.charges.create({ amount, currency: 'chf' });
stripeSpan.end();
});
// Incrémenter le compteur
paymentCounter.add(1, { status: 'success', currency: 'CHF' });
span.setStatus({ code: SpanStatusCode.OK });
return result;
} catch (error) {
// Enregistrer l'erreur
span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });
span.recordException(error);
paymentCounter.add(1, { status: 'error', currency: 'CHF' });
throw error;
} finally {
paymentDuration.record(Date.now() - startTime);
span.end();
}
});
}
Python :
from opentelemetry import trace, metrics
tracer = trace.get_tracer("payment-service")
meter = metrics.get_meter("payment-service")
payment_counter = meter.create_counter(
"payments_processed_total",
description="Total payments processed",
)
payment_duration = meter.create_histogram(
"payment_duration_ms",
description="Payment processing duration",
)
def process_payment(user_id: str, amount: float):
with tracer.start_as_current_span("process-payment") as span:
span.set_attribute("user.id", user_id)
span.set_attribute("payment.amount", amount)
try:
with tracer.start_as_current_span("db.insert_payment") as db_span:
db_span.set_attribute("db.system", "postgresql")
result = db.insert_payment(user_id, amount)
with tracer.start_as_current_span("stripe.charge") as stripe_span:
stripe_span.set_attribute("http.url", "https://api.stripe.com/v1/charges")
stripe.Charge.create(amount=amount, currency="chf")
payment_counter.add(1, {"status": "success"})
return result
except Exception as e:
span.set_status(trace.StatusCode.ERROR, str(e))
span.record_exception(e)
payment_counter.add(1, {"status": "error"})
raise
Le OTel Collector
Le Collector est le composant central de l’architecture OTel. Il reçoit, traite et exporte les données de télémétrie.
Apps → [Receivers] → [Processors] → [Exporters] → Backends
Configuration
otel-collector-config.yaml :
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
# Scraper Prometheus (métriques existantes)
prometheus:
config:
scrape_configs:
- job_name: 'kubernetes-pods'
scrape_interval: 15s
kubernetes_sd_configs:
- role: pod
# Host metrics (CPU, RAM, disk)
hostmetrics:
collection_interval: 30s
scrapers:
cpu: {}
memory: {}
disk: {}
network: {}
processors:
# Batch pour optimiser les exports
batch:
timeout: 5s
send_batch_size: 1000
send_batch_max_size: 2000
# Ajouter des attributs
resource:
attributes:
- key: environment
value: production
action: upsert
- key: cluster
value: prod-eu-west
action: upsert
# Filtrer (réduire le volume)
filter:
error_mode: ignore
traces:
span:
- 'attributes["http.target"] == "/health"' # Exclure les health checks
metrics:
metric:
- 'name == "go_gc_duration_seconds"' # Exclure les métriques Go internes
# Sampling (tail-based)
tail_sampling:
decision_wait: 10s
policies:
- name: errors
type: status_code
status_code: {status_codes: [ERROR]}
- name: slow-requests
type: latency
latency: {threshold_ms: 1000}
- name: probabilistic
type: probabilistic
probabilistic: {sampling_percentage: 10}
# Memory limiter (protection OOM)
memory_limiter:
check_interval: 1s
limit_mib: 512
spike_limit_mib: 128
exporters:
# Grafana Tempo (traces)
otlp/tempo:
endpoint: tempo:4317
tls:
insecure: true
# Grafana Mimir (métriques)
prometheusremotewrite:
endpoint: http://mimir:9009/api/v1/push
# Grafana Loki (logs)
loki:
endpoint: http://loki:3100/loki/api/v1/push
# Debug (stdout)
debug:
verbosity: basic
extensions:
health_check:
endpoint: 0.0.0.0:13133
zpages:
endpoint: 0.0.0.0:55679
service:
extensions: [health_check, zpages]
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, resource, filter, tail_sampling, batch]
exporters: [otlp/tempo]
metrics:
receivers: [otlp, prometheus, hostmetrics]
processors: [memory_limiter, resource, filter, batch]
exporters: [prometheusremotewrite]
logs:
receivers: [otlp]
processors: [memory_limiter, resource, batch]
exporters: [loki]
Stack LGTM (Loki, Grafana, Tempo, Mimir)
La stack LGTM de Grafana Labs est le backend open source le plus populaire pour OpenTelemetry :
| Composant | Rôle | Équivalent propriétaire |
|---|---|---|
| Loki | Stockage de logs | Splunk, Elasticsearch |
| Grafana | Visualisation | Datadog dashboards |
| Tempo | Stockage de traces | Jaeger, Zipkin |
| Mimir | Stockage de métriques | Prometheus long-term, Thanos |
Architecture
┌──────────────┐
│ Grafana │ ← Visualisation
│ :3000 │
└──────┬───────┘
│ query
┌──────────┬───────┼───────────┐
│ │ │ │
┌────▼───┐ ┌───▼───┐ ┌─▼──────┐ ┌──▼───┐
│ Loki │ │ Tempo │ │ Mimir │ │Pyro- │
│ (logs) │ │(trace)│ │(metric)│ │scope │
│ :3100 │ │ :3200 │ │ :9009 │ │:4040 │
└────▲───┘ └───▲───┘ └───▲────┘ └──▲───┘
│ │ │ │
└─────────┴────┬────┴──────────┘
│
┌────────▼─────────┐
│ OTel Collector │ ← Réception & routage
│ :4317 / :4318 │
└────────▲─────────┘
│ OTLP
┌──────┬───────┼───────┬──────┐
│ │ │ │ │
App1 App2 App3 App4 App5
Démo pratique avec Docker Compose
On va monter toute la stack : apps instrumentées → OTel Collector → LGTM → Grafana.
docker-compose.yml
services:
# ============================================
# Backend d'observabilité
# ============================================
grafana:
image: grafana/grafana:11.4.0
ports:
- "3000:3000"
environment:
GF_SECURITY_ADMIN_USER: admin
GF_SECURITY_ADMIN_PASSWORD: devopslab
GF_AUTH_ANONYMOUS_ENABLED: "true"
GF_AUTH_ANONYMOUS_ORG_ROLE: Viewer
volumes:
- ./config/grafana/provisioning:/etc/grafana/provisioning
- grafana-data:/var/lib/grafana
depends_on:
- loki
- tempo
- mimir
loki:
image: grafana/loki:3.3.0
ports:
- "3100:3100"
command: -config.file=/etc/loki/config.yaml
volumes:
- ./config/loki/config.yaml:/etc/loki/config.yaml
- loki-data:/loki
tempo:
image: grafana/tempo:2.6.0
ports:
- "3200:3200" # HTTP API
- "4317:4317" # OTLP gRPC (direct, sans collector)
command: -config.file=/etc/tempo/config.yaml
volumes:
- ./config/tempo/config.yaml:/etc/tempo/config.yaml
- tempo-data:/var/tempo
mimir:
image: grafana/mimir:2.14.0
ports:
- "9009:9009"
command: -config.file=/etc/mimir/config.yaml
volumes:
- ./config/mimir/config.yaml:/etc/mimir/config.yaml
- mimir-data:/data
# ============================================
# OpenTelemetry Collector
# ============================================
otel-collector:
image: otel/opentelemetry-collector-contrib:0.115.0
ports:
- "4318:4318" # OTLP HTTP
- "13133:13133" # Health check
- "55679:55679" # zPages
volumes:
- ./config/otel-collector/config.yaml:/etc/otelcol-contrib/config.yaml
depends_on:
- loki
- tempo
- mimir
# ============================================
# Applications de démo
# ============================================
api-gateway:
build:
context: ./apps/api-gateway
ports:
- "8080:8080"
environment:
OTEL_SERVICE_NAME: api-gateway
OTEL_EXPORTER_OTLP_ENDPOINT: http://otel-collector:4318
PAYMENT_SERVICE_URL: http://payment-service:8081
USER_SERVICE_URL: http://user-service:8082
depends_on:
- otel-collector
- payment-service
- user-service
payment-service:
build:
context: ./apps/payment-service
ports:
- "8081:8081"
environment:
OTEL_SERVICE_NAME: payment-service
OTEL_EXPORTER_OTLP_ENDPOINT: http://otel-collector:4318
DB_HOST: postgres
DB_NAME: payments
depends_on:
- otel-collector
- postgres
user-service:
build:
context: ./apps/user-service
ports:
- "8082:8082"
environment:
OTEL_SERVICE_NAME: user-service
OTEL_EXPORTER_OTLP_ENDPOINT: http://otel-collector:4318
REDIS_URL: redis://redis:6379
depends_on:
- otel-collector
- redis
# ============================================
# Datastores
# ============================================
postgres:
image: postgres:16
environment:
POSTGRES_DB: payments
POSTGRES_USER: app
POSTGRES_PASSWORD: secret
volumes:
- pg-data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
volumes:
- redis-data:/data
# ============================================
# Générateur de trafic
# ============================================
load-generator:
build:
context: ./apps/load-generator
environment:
TARGET_URL: http://api-gateway:8080
REQUESTS_PER_SECOND: 5
depends_on:
- api-gateway
volumes:
grafana-data:
loki-data:
tempo-data:
mimir-data:
pg-data:
redis-data:
Configurations des backends
config/loki/config.yaml :
auth_enabled: false
server:
http_listen_port: 3100
common:
path_prefix: /loki
storage:
filesystem:
chunks_directory: /loki/chunks
rules_directory: /loki/rules
replication_factor: 1
ring:
kvstore:
store: inmemory
schema_config:
configs:
- from: "2024-01-01"
store: tsdb
object_store: filesystem
schema: v13
index:
prefix: index_
period: 24h
limits_config:
allow_structured_metadata: true
volume_enabled: true
config/tempo/config.yaml :
server:
http_listen_port: 3200
distributor:
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
storage:
trace:
backend: local
local:
path: /var/tempo/traces
wal:
path: /var/tempo/wal
metrics_generator:
registry:
external_labels:
source: tempo
cluster: docker-compose
storage:
path: /var/tempo/generator/wal
remote_write:
- url: http://mimir:9009/api/v1/push
send_exemplars: true
traces_storage:
path: /var/tempo/generator/traces
processor:
service_graphs:
dimensions:
- service.namespace
span_metrics:
dimensions:
- http.method
- http.status_code
overrides:
defaults:
metrics_generator:
processors: [service-graphs, span-metrics]
config/mimir/config.yaml :
multitenancy_enabled: false
server:
http_listen_port: 9009
blocks_storage:
backend: filesystem
filesystem:
dir: /data/blocks
tsdb:
dir: /data/tsdb
compactor:
data_dir: /data/compactor
distributor:
ring:
kvstore:
store: memberlist
ingester:
ring:
kvstore:
store: memberlist
replication_factor: 1
store_gateway:
sharding_ring:
replication_factor: 1
limits:
native_histograms_ingestion_enabled: true
config/otel-collector/config.yaml :
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
batch:
timeout: 5s
send_batch_size: 512
resource:
attributes:
- key: deployment.environment
value: demo
action: upsert
memory_limiter:
check_interval: 1s
limit_mib: 256
exporters:
otlp/tempo:
endpoint: tempo:4317
tls:
insecure: true
prometheusremotewrite:
endpoint: http://mimir:9009/api/v1/push
resource_to_telemetry_conversion:
enabled: true
loki:
endpoint: http://loki:3100/loki/api/v1/push
debug:
verbosity: basic
extensions:
health_check:
endpoint: 0.0.0.0:13133
service:
extensions: [health_check]
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, resource, batch]
exporters: [otlp/tempo]
metrics:
receivers: [otlp]
processors: [memory_limiter, resource, batch]
exporters: [prometheusremotewrite]
logs:
receivers: [otlp]
processors: [memory_limiter, resource, batch]
exporters: [loki]
config/grafana/provisioning/datasources/datasources.yaml :
apiVersion: 1
datasources:
- name: Mimir
type: prometheus
access: proxy
url: http://mimir:9009/prometheus
isDefault: true
jsonData:
exemplarTraceIdDestinations:
- name: traceID
datasourceUid: tempo
- name: Tempo
type: tempo
access: proxy
uid: tempo
url: http://tempo:3200
jsonData:
tracesToLogs:
datasourceUid: loki
filterByTraceID: true
tracesToMetrics:
datasourceUid: mimir
serviceMap:
datasourceUid: mimir
- name: Loki
type: loki
access: proxy
uid: loki
url: http://loki:3100
jsonData:
derivedFields:
- datasourceUid: tempo
matcherRegex: '"trace_id":"(\w+)"'
name: TraceID
url: '$${__value.raw}'
Lancer la démo
# Tout démarrer
docker compose up -d
# Vérifier que tout tourne
docker compose ps
# Ouvrir Grafana
open http://localhost:3000
# Login: admin / devopslab
# Générer du trafic
curl http://localhost:8080/api/users
curl http://localhost:8080/api/payments -X POST -d '{"amount": 42}'
# Voir les traces dans Grafana
# → Explore → Tempo → Search
# → Service Map (les connexions entre services)
# Voir les logs
# → Explore → Loki → {service_name="payment-service"}
# Voir les métriques
# → Explore → Mimir → http_server_request_duration_seconds_bucket
Ce que tu vas voir dans Grafana
- Service Map — Graphe automatique des connexions entre tes services
- Traces — Le parcours complet d’une requête avec les timings
- Logs corrélés — Cliquer sur une trace ouvre les logs associés
- RED metrics — Rate, Errors, Duration générés automatiquement par Tempo
- Exemplars — Points sur les graphiques qui linkent vers la trace correspondante
Bonnes pratiques
Sampling intelligent
Tu ne peux pas stocker 100% des traces en production. Utilise le sampling :
# Head-based sampling (décision au début de la trace)
# Simple mais peut rater des traces intéressantes
sampler:
type: parentbased_traceidratio
argument: "0.1" # 10% des traces
# Tail-based sampling (décision à la fin, dans le Collector)
# Plus coûteux mais garde les traces intéressantes
processors:
tail_sampling:
policies:
- name: keep-errors
type: status_code
status_code: {status_codes: [ERROR]} # 100% des erreurs
- name: keep-slow
type: latency
latency: {threshold_ms: 500} # 100% des requêtes lentes
- name: sample-rest
type: probabilistic
probabilistic: {sampling_percentage: 5} # 5% du reste
Conventions de nommage (Semantic Conventions)
OTel définit des conventions standard pour les attributs :
# HTTP
http.method = "GET"
http.status_code = 200
http.url = "https://api.devopslab.ch/users"
# Database
db.system = "postgresql"
db.name = "payments"
db.statement = "SELECT * FROM users WHERE id = ?"
# Service
service.name = "payment-api"
service.version = "2.4.0"
service.namespace = "production"
# Infrastructure
host.name = "web01"
container.id = "abc123"
k8s.pod.name = "payment-api-7f8d9b-x2k4l"
Coûts à surveiller
| Signal | Volume typique | Coût stockage |
|---|---|---|
| Métriques | ~1-5K series/service | Faible |
| Logs | ~1-10 GB/jour/service | Élevé |
| Traces | ~100K-1M spans/jour | Moyen |
Réduire les coûts :
- Sampling des traces (10-25% suffit souvent)
- Filtrer les health checks et les métriques inutiles
- Rétention adaptée (traces 7j, métriques 90j, logs 30j)
- Compression (Loki et Tempo compressent nativement)
Conclusion
OpenTelemetry est devenu le standard de facto pour l’observabilité en 2026. Avec la stack LGTM, tu as une solution complète, open source, et sans vendor lock-in.
Par où commencer :
- Ajoute l’auto-instrumentation à une app existante (5 minutes)
- Déploie un OTel Collector pour centraliser les données
- Monte Grafana + Tempo pour voir tes premières traces
- Ajoute Loki pour les logs, Mimir pour les métriques long-term
- Crée des dashboards et des alertes
L’observabilité n’est pas un luxe — c’est ce qui fait la différence entre “ça marche” et “je sais pourquoi ça marche (ou pas)”.
Sur cette page
Articles liés
Monitoring avec Prometheus et Grafana : guide pratique de A à Z
Guide complet pour mettre en place un stack de monitoring avec Prometheus et Grafana. Installation Docker, configuration, PromQL, dashboards et alerting — tout y est, étape par étape.
Datadog vs New Relic vs Grafana : quel outil de monitoring choisir en 2026 ?
Datadog vs New Relic vs Grafana : comparatif honnête pour choisir le meilleur outil de monitoring en 2026.
Comparatif hébergement cloud 2026 : AWS vs GCP vs Azure vs alternatives
AWS vs GCP vs Azure vs alternatives : comparatif complet des hébergements cloud en 2026 avec pricing détaillé.