Guide d'intégration
perf-sentinel accepte les traces OpenTelemetry via OTLP (gRPC sur 4317, HTTP sur 4318). Ce guide vous accompagne de zéro jusqu'à votre premier finding pour chaque topologie de déploiement.
Voir aussi. Pas familier d'OpenTelemetry ? L'introduction à OpenTelemetry définit OTLP, le Collector, les spans et les traces en quelques paragraphes. À lire en premier si un de ces termes ne vous est pas familier.
Sommaire
- Choisissez votre topologie : tableau comparatif des quatre modes de déploiement supportés.
- Démarrage rapide : CI batch : exécuter perf-sentinel depuis un pipeline CI contre un fixture de traces.
- Démarrage rapide : collector central : déploiement production via OpenTelemetry Collector.
- Vous venez de Datadog : faire le pont avec le trafic dd-trace via un OpenTelemetry Collector quand vous n'avez pas d'instrumentation OTel.
- Démarrage rapide : sidecar : debug d'un seul service en dev ou staging.
- Démarrage rapide : daemon direct : développement local.
- Pour aller plus loin : pointeurs vers Instrumentation et CI pour les sujets côté application et côté CI.
- Formats d'ingestion : règles d'auto-détection JSON natif, OTLP, Jaeger, Zipkin, Tempo, pg_stat_statements et performance_schema MySQL.
- Mode explain : vue arborescente d'une trace.
- Export SARIF : sortie SARIF v2.1.0 pour le code scanning GitHub ou GitLab.
- Champ de confiance sur les findings : champ
confidenceJSON / SARIF pour les consommateurs aval. - API de requêtage du daemon : API HTTP sur le port OTLP HTTP, voir aussi API de query pour la référence complète.
- Configuration avancée du scoring carbone : scoring multi-région, Scaphandre, énergie cloud-native, Electricity Maps, calibration.
- Intégration Tempo : interroger un backend Grafana Tempo directement avec
perf-sentinel tempo. - Intégration API Jaeger query : Jaeger upstream et Victoria Traces via une seule sous-commande.
- Troubleshooting : problèmes d'ingestion et de détection courants.
Choisissez votre topologie
| Topologie | Idéal pour | Effort | Modifications des services |
|---|---|---|---|
| CI batch | Pipelines CI, vérifications de PR | Le plus faible | Aucune (fichiers de traces) |
| Collector central | Production, multi-services | Faible | Aucune (config YAML uniquement) |
| Sidecar | Dev/staging, debug d'un service | Faible | Aucune (Docker uniquement) |
| Daemon direct | Dev local, expérimentations | Moyen | Variables d'env par langage |
Démarrage rapide : CI batch
Cas d'usage : exécuter perf-sentinel dans votre pipeline CI pour détecter les requêtes N+1 avant la production. Pas de daemon, pas de Docker, un binaire qui lit un fichier de traces et retourne le code 1 si le quality gate échoue.
Étape 1 : Installation
curl -LO https://github.com/robintra/perf-sentinel/releases/latest/download/perf-sentinel-linux-amd64
chmod +x perf-sentinel-linux-amd64
sudo mv perf-sentinel-linux-amd64 /usr/local/bin/perf-sentinelÉtape 2 : Configurer les seuils
Créez .perf-sentinel.toml à la racine de votre projet :
[thresholds]
n_plus_one_sql_critical_max = 0 # zéro tolérance pour les N+1 SQL
io_waste_ratio_max = 0.30 # max 30% d'I/O évitables
[detection]
n_plus_one_min_occurrences = 5
slow_query_threshold_ms = 500
[green]
enabled = true
default_region = "eu-west-3" # optionnel : active les estimations gCO2eq
# : surcharges par service pour les déploiements multi-région
# [green.service_regions]
# "api-us" = "us-east-1"
# "api-asia" = "ap-southeast-1"La sortie CO₂ est structurée :green_summary.co2.total.{low,mid,high}plus un tag de méthodologie SCI v1.0, avec un intervalle d'incertitude multiplicative 2× (low = mid/2,high = mid×2). Le scoring multi-région est automatique quand les spans OTel portent l'attributcloud.region. Voir Configuration et Limites pour les détails.
Étape 3 : Collecter les traces
Exportez les traces depuis vos tests d'intégration. Si vos tests tournent avec l'instrumentation OTel, sauvegardez la sortie dans un fichier JSON. Vous pouvez aussi exporter depuis l'UI Jaeger ou Zipkin, perf-sentinel détecte automatiquement le format.
Étape 4 : Analyser
perf-sentinel analyze --ci --input traces.json --config .perf-sentinel.tomlLe processus affiche un rapport JSON sur stdout et retourne le code 0 (succès) ou 1 (échec). Ajoutez ceci à votre job CI :
# Exemple GitLab CI
perf:sentinel:
stage: quality
script:
- perf-sentinel analyze --ci --input traces.json --config .perf-sentinel.toml
artifacts:
paths: [perf-sentinel-report.json]
when: always
allow_failure: true # commencez en warning, retirez une fois les seuils calibrésÉtape 5 : Investiguer les findings
# Rapport coloré en terminal
perf-sentinel analyze --input traces.json --config .perf-sentinel.toml
# Vue arborescente d'une trace spécifique
perf-sentinel explain --input traces.json --trace-id <trace-id>
# TUI interactif
perf-sentinel inspect --input traces.json
# SARIF pour GitHub/GitLab code scanning
perf-sentinel analyze --input traces.json --format sarif > results.sarif
# Dashboard HTML single-file pour l'exploration post-mortem en navigateur
perf-sentinel report --input traces.json --output report.htmlDashboard HTML
perf-sentinel report --input traces.json --output report.html produit un dashboard HTML single-file. Double-clic pour l'ouvrir dans n'importe quel navigateur, fonctionne hors ligne, sans ressource externe. Public visé : les devs qui explorent un artefact CI et préfèrent cliquer plutôt que taper. Le dashboard affiche les findings, les arbres de traces et les métriques GreenOps avec navigation croisée entre sections (clic sur un finding pour voir son arbre de trace, la span responsable est surlignée en rouge).
Flags :
--input <FICHIER>ou--input -: fichier de traces ou stdin (même auto-détection de format queanalyze: JSON natif, OTLP JSON, Jaeger, Zipkin v2).--output <FICHIER>: requis, écrasé s'il existe déjà.--config <CHEMIN>:.perf-sentinel.tomloptionnel, mêmes sémantiques queanalyze --config.--max-traces-embedded <N>: cap sur les traces embarquées pour l'onglet Explain. Sans valeur, la sortie est ajustée automatiquement pour viser une taille HTML d'environ 5 Mo en coupant les traces à plus faible IIS. Un bandeau dans l'onglet Findings remonte le ratio tronqué quand la coupe s'applique, et la CLI loggue une ligneinfo!sur stderr (Embedded N of M traces ... trimmed for file size) pour que les logs de build CI portent le même signal.--pg-stat <FICHIER>: cross-référence un exportpg_stat_statementsCSV ou JSON. Active l'onglet pg_stat et la navigation croisée Explain vers pg_stat sur les spans SQL dont le template normalisé correspond à une ligne pg_stat.--pg-stat-prometheus <URL>: scrape one-shot d'unpostgres_exporter, même effet que--pg-statsans le fichier intermédiaire. Mutuellement exclusif avec--pg-stat.--pg-stat-auth-header "Nom: Valeur": en-tête d'authentification optionnel attaché à la requête--pg-stat-prometheus(même format"Nom: Valeur"que le flag--auth-headerdes sous-commandestempoetjaeger-query). La variable d'environnementPERF_SENTINEL_PGSTAT_AUTH_HEADERest prioritaire sur le flag. Préférez la variable d'environnement en production pour éviter d'exposer le secret dans la liste des arguments de processus ou l'historique shell. Quand la valeur est fournie via le flag et que la variable d'environnement est absente, un warning de démarrage oriente vers la variable d'environnement. Requis pour Grafana Cloud, Grafana Mimir ou tout ingress Prometheus appliquant une auth basic ou bearer. La valeur est marquéesensitivepour que hyper la masque dans les logs debug et les tables HPACK. L'envoyer en clair viahttp://déclenche untracing::warn!, préférezhttps://en production.--mysql-stat <FICHIER>: embarque un exportevents_statements_summary_by_digest(MySQL Performance Schema) CSV ou JSON. Active l'onglet mysql_stat avec le même sous-sélecteur de rankings que pg_stat (quatrième ranking : lignes examinées).--mysql-stat-top <N>ajuste la taille des rankings (défaut 10).--before <FICHIER>: rapport baseline JSON (la sortie deanalyze --format json). Active un onglet Diff qui affiche les nouveaux findings, les findings résolus, les changements de sévérité et les deltas d'I/O par endpoint par rapport à la baseline.
Les codes de sortie diffèrent de analyze --ci : report sort toujours 0, même quand la quality gate échoue. Le statut de la gate est rendu comme un badge dans la barre supérieure du HTML. Utilise analyze --ci quand tu as besoin du signal d'exit-code en CI.
Exemples d'invocation :
# Rapport post-mortem de base
perf-sentinel report --input traces.json --output report.html
# Avec cross-référence hotspots SQL
perf-sentinel report --input traces.json \
--pg-stat pg_stat_statements.csv \
--output report.html
# Avec scrape Prometheus à la place du fichier
perf-sentinel report --input traces.json \
--pg-stat-prometheus http://prometheus:9090 \
--output report.html
# Vue régression PR : diff contre une baseline
perf-sentinel report --input after.json \
--before before.json \
--output report.html
# Tout à la fois
perf-sentinel report --input traces.json \
--pg-stat pg_stat_statements.csv \
--before baseline.json \
--output report.htmlLe dashboard HTML est documenté dans Rapport HTML, avec la liste complète des options, les raccourcis clavier, la recherche, l'export CSV et le pipeline d'instantané /api/export/report du démon en mode live. Dans le dashboard, ? ouvre la cheatsheet qui liste tous les raccourcis.
Partage et export : chaque onglet listable (Findings, pg_stat, Diff, Correlations) expose un bouton Export CSV qui télécharge la vue filtrée active au format CSV RFC 4180 (les templates contenant virgules ou guillemets sont échappés correctement, plus un guard OWASP formula-injection qui préfixe une apostrophe sur les cellules commençant par =, +, -, @ ou une tabulation). Le fragment d'URL reflète l'onglet actif plus la recherche et les puces de filtre, donc partager un lien comme report.html#pgstat&ranking=mean_time&search=payment restaure exactement la même vue chez le destinataire. Le thème et le dernier classement pg_stat sélectionné persistent dans sessionStorage, limité à l'onglet de navigateur courant.
C'est une vue post-mortem d'un jeu de traces terminé. Pour une inspection live d'un daemon qui tourne, utilise perf-sentinel query inspect (TUI) ou directement les endpoints /api/*. Pour un workflow Tempo, compose via le shell : perf-sentinel tempo --endpoint http://tempo:3200 --search "..." --output traces.json && perf-sentinel report --input traces.json --output report.html.
Snapshot depuis un daemon live
Quand un daemon tourne, GET /api/export/report émet son état courant sous forme de JSON Report, avec la même forme que analyze --format json. Pipe le directement dans le dashboard pour un snapshot quasi-live (toujours post-mortem au sens sémantique, juste à courte durée de vie) :
curl -s http://daemon.internal:4318/api/export/report \
| perf-sentinel report --input - --output report.htmlreport --input auto-détecte la forme JSON : un tableau au top-level est traité comme des événements de trace et passe par normalize/detect/score, un objet est traité comme un Report pré-calculé et embarqué tel quel. Seuls les Reports produits par le daemon portent des correlations, donc l'onglet Correlations du dashboard s'active automatiquement quand ce chemin est emprunté. Les daemons en cold-start retournent 503 avec {"error": "daemon has not yet processed any events"} jusqu'à ce que le premier batch OTLP arrive.
Démarrage rapide : collector central
Cas d'usage : déploiement production où les services envoient déjà des traces à un OpenTelemetry Collector (ou vous souhaitez en ajouter un). Zéro modification de code, uniquement de la configuration YAML.
Étape 1 : Démarrer perf-sentinel + collector
# Récupérer le fichier compose d'exemple (sans cloner le dépôt), puis le démarrer
curl -o docker-compose.yml https://raw.githubusercontent.com/robintra/perf-sentinel/main/examples/docker-compose-collector.yml
docker compose up -dL'URL suit main. Remplacez main par un tag de release (par exemple v0.8.13) pour figer une version précise.
Cela démarre :
- Un OTel Collector qui écoute sur les ports 4317 (gRPC) et 4318 (HTTP)
- perf-sentinel en mode watch, recevant les traces du collector
Étape 2 : Pointer vos services vers le collector
Définissez ces variables d'environnement dans vos conteneurs applicatifs :
OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317
OTEL_EXPORTER_OTLP_PROTOCOL=grpcSi vos services exportent déjà vers un collector, ajoutez perf-sentinel comme exporteur supplémentaire dans votre otel-collector-config.yaml existant :
exporters:
otlp/perf-sentinel:
endpoint: perf-sentinel:4317
tls:
insecure: true
service:
pipelines:
traces:
exporters: [otlp/perf-sentinel, otlp/votre-backend-existant]Étape 3 : Générer du trafic
Utilisez votre application normalement. Après l'expiration du TTL des traces (30 secondes par défaut), perf-sentinel émet les findings en NDJSON sur stdout :
docker compose logs -f perf-sentinelÉtape 4 : Monitoring avec Prometheus + Grafana
perf-sentinel expose des métriques Prometheus à http://localhost:14318/metrics avec des exemplars OpenMetrics (clic direct depuis Grafana vers votre backend de traces) :
curl -s http://localhost:14318/metrics | grep perf_sentinelAjoutez-le comme cible de scrape Prometheus :
# prometheus.yml
scrape_configs:
- job_name: perf-sentinel
static_configs:
- targets: ['perf-sentinel:4318']Métriques clés :
perf_sentinel_findings_total{type, severity}: findings avec exemplartrace_idpour le clic directperf_sentinel_io_waste_ratio: ratio de gaspillage I/O avec exemplartrace_idperf_sentinel_events_processed_total: total de spans ingérésperf_sentinel_traces_analyzed_total: total de traces complétéesperf_sentinel_slow_duration_seconds{type}: histogram des durées de spans lents (utiliserhistogram_quantile()pour des percentiles globaux sur des instances shardées)
Voir examples/otel-collector-config.yaml pour la config complète avec les options de sampling et filtrage.
Vous venez de Datadog (dd-trace, sans OpenTelemetry)
perf-sentinel ingère de l'OTLP, pas le format APM natif de Datadog, et n'embarque aucun adaptateur Datadog par choix. Si vos services sont instrumentés avec dd-trace (le traceur propriétaire de Datadog) et que vous n'avez pas d'instrumentation OpenTelemetry, faites le pont avec un OpenTelemetry Collector équipé du datadogreceiver. Ce receiver implémente l'API d'intake de traces de l'agent Datadog sur le port 8126, convertit les spans dd-trace en OTLP, et un exporter otlp transmet une copie à perf-sentinel. Aucun changement de code applicatif n'est requis : vous repointez dd-trace vers le Collector, et vous pouvez continuer à envoyer vers Datadog en parallèle.
perf-sentinel lit nativement la ressource Datadog (dd.span.Resource, où dd-trace laisse le SQL obfusqué), donc la détection SQL fonctionne tant que chaque span conserve un signal base de données : la clé stable db.system.name (ce qu'émettent les versions récentes du receiver), l'ancienne db.system, ou le tag dd-trace db.type. Aucun remappage d'attributs dans le Collector n'est nécessaire. Si une version du Collector les retire toutes, ajoutez un processor transform qui en restaure une pour que le garde-fou se déclenche.
Réserve sur N+1 contre requêtes redondantes. dd-trace pré-obfusque le SQL (les littéraux sont déjà ?), donc les paramètres par requête qui distinguent un N+1 d'une requête légitimement répétée ont disparu avant que perf-sentinel ne les voie. En mode de détection auto par défaut, un vrai N+1 à timing uniforme peut apparaître comme redundant_sql plutôt que n_plus_one_sql. Réglez [detection] sanitizer_aware_classification = "strict" pour récupérer les cas à forte occurrence (3 fois le n_plus_one_threshold configuré, soit 15 ou plus à la valeur par défaut de 5). Le scoring de gaspillage et de carbone est identique pour les deux types de finding.
PHP (Laravel, Symfony) via dd-trace-php. La même réserve d'obfuscation s'applique : un vrai N+1 Laravel ou Symfony peut apparaître comme redundant_sql en mode auto, utilisez donc sanitizer_aware_classification = "strict". Le pont perd aussi le signal de framework : le datadogreceiver pose un scope d'instrumentation Datadog fixe et ne mappe aucun attribut code.*, donc les findings dd-trace-php n'ont pas de suggested_fix conscient du framework (ils retombent sur php_generic ou restent non enrichis). Les fixes spécifiques Laravel/Eloquent et Symfony/Doctrine exigent l'instrumentation OpenTelemetry PHP native (scopes io.opentelemetry.contrib.php.*), voir Instrumentation.
Un piège courant : la mention "OpenTelemetry compliant" de Datadog désigne le plus souvent l'agent Datadog qui ingère de l'OTLP (entrant, depuis des SDK OTel), ce qui est le sens inverse. Faire sortir les données dd-trace vers un backend OTLP passe par le chemin Collector ci-dessous.
Stabilité. Le support des traces du datadogreceiver est en alpha dans opentelemetry-collector-contrib (en 2026). Il convient à l'évaluation et aux preuves de concept. Surveillez-le si vous le maintenez en permanence devant la production.
Config du Collector (dd-trace en entrée, OTLP en sortie vers perf-sentinel) :
receivers:
datadog:
endpoint: 0.0.0.0:8126 # intake APM dd-trace
exporters:
otlp/perf-sentinel:
endpoint: perf-sentinel:4317
tls:
insecure: true
# datadog: # optionnel : garder votre backend Datadog existant
# api:
# key: ${DD_API_KEY}
service:
pipelines:
traces:
receivers: [datadog]
exporters: [otlp/perf-sentinel] # ajouter `datadog` ici pour dupliquer vers les deuxPointez dd-trace vers le Collector au lieu de l'agent Datadog :
DD_TRACE_AGENT_URL=http://otel-collector:8126perf-sentinel reçoit alors une copie de chaque trace et les findings sont émis en NDJSON, exactement comme dans la topologie collector central ci-dessus.
Batch plutôt que le daemon. Le chemin Collector ci-dessus alimente le daemon watch. Pour un analyze ponctuel à la place, la voie la plus simple depuis la 0.9.5 est l'exporter file du Collector : sa sortie OTLP JSON (objet unique ou NDJSON, une requête par ligne) est auto-détectée par analyze --input comme n'importe quel fichier de traces. Sinon, routez le pont vers un backend Jaeger ou Tempo et tirez-en un lot :
perf-sentinel jaeger-query --endpoint http://jaeger:16686 --service checkout --lookback 15m
perf-sentinel tempo --endpoint http://tempo:3200 --service checkout --lookback 15mUne trace exportée au format JSON Jaeger depuis l'UI Jaeger (bouton "Download JSON") fonctionne aussi avec analyze --input. Quelle que soit la voie batch, la réserve d'obfuscation ci-dessus reste valable, gardez sanitizer_aware_classification = "strict".
Démarrage rapide : sidecar
Cas d'usage : debug d'un service unique en dev/staging. perf-sentinel tourne à côté du service, partageant son namespace réseau.
Étape 1 : Démarrer le sidecar
# Récupérer le fichier compose d'exemple (sans cloner le dépôt), puis le démarrer
curl -o docker-compose.yml https://raw.githubusercontent.com/robintra/perf-sentinel/main/examples/docker-compose-sidecar.yml
docker compose up -dÉtape 2 : Configurer votre app
Votre app envoie les traces à localhost:4318 (HTTP), pas de saut réseau puisque perf-sentinel partage le même namespace réseau :
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobufÉtape 3 : Voir les findings
docker compose logs -f perf-sentinelVoir examples/docker-compose-sidecar.yml pour la configuration complète.
Démarrage rapide : daemon direct
Cas d'usage : développement local. Exécutez perf-sentinel sur votre machine et pointez vos services vers lui.
Étape 1 : Démarrer le daemon
perf-sentinel watchPar défaut, il écoute sur 127.0.0.1:4317 (gRPC) et 127.0.0.1:4318 (HTTP). Pour que les conteneurs Docker puissent atteindre l'hôte, utilisez :
# .perf-sentinel.toml
[daemon]
listen_address = "0.0.0.0"Un bind non-loopback logue un avertissement au démarrage : les endpoints n'ont pas d'auth applicative, mettez donc un reverse-proxy ou une network policy en frontal, ou posez [daemon.ack] api_key pour garder les écritures d'ack (voir Configuration).
Étape 2 : Instrumenter votre service
Définissez l'endpoint OTLP dans votre service (voir les guides par langage ci-dessous) :
# Pour les services sur l'hôte
OTEL_EXPORTER_OTLP_ENDPOINT=http://127.0.0.1:4317
# Pour les services dans Docker
OTEL_EXPORTER_OTLP_ENDPOINT=http://host.docker.internal:4317Étape 3 : Voir les findings
Les findings sont émis sur stdout en NDJSON. Les métriques Prometheus sont disponibles à http://localhost:4318/metrics.
Pour aller plus loin
Les quatre démarrages rapides ci-dessus mènent à un setup fonctionnel. Deux guides compagnons couvrent la suite :
- Instrumentation : comment envoyer des données à perf-sentinel. Instrumentation par langage (Java, Quarkus, .NET, Rust), chemin OTel Collector production avec guidance sampling, intégrations cloud, manifests Kubernetes.
- CI : comment câbler perf-sentinel dans la CI. Invocation en mode batch, recettes copier-coller pour GitHub Actions / GitLab CI / Jenkins, philosophie du quality gate, chemin de déploiement du rapport HTML interactif par provider, et la sous-commande
diffpour la détection de régressions sur PR.
Les sections de référence ci-dessous restent dans ce document parce qu'elles s'appliquent à toutes les topologies (formats d'entrée et de sortie, API HTTP du daemon, scoring carbone avancé, ingestion Tempo et Jaeger, troubleshooting).
Formats d'ingestion
perf-sentinel auto-détecte le format d'entrée avec perf-sentinel analyze --input :
| Format | Détection | Exemple |
|---|---|---|
| Natif (perf-sentinel JSON) | Tableau d'objets avec champ "type" | Format par défaut |
| OTLP JSON | Objet avec clé "resourceSpans" | Dump exporter file du Collector |
| Jaeger JSON | Objet avec clé "data" contenant "spans" | Exporté depuis l'UI Jaeger |
| Zipkin JSON v2 | Tableau d'objets avec "traceId" + "localEndpoint" | Exporté depuis l'UI Zipkin |
Aucun flag --format n'est nécessaire pour l'entrée : le format est détecté automatiquement depuis les premiers octets du fichier.
L'OTLP JSON en batch (0.9.5+). analyze --input accepte la forme OTLP/JSON du protocole (ExportTraceServiceRequest, clés camelCase, ids trace/span en hexadécimal), en objet unique comme en NDJSON de l'exporter file du Collector (une requête par ligne). L'OTLP atteint aussi perf-sentinel en live par les listeners du daemon (watch) et indirectement par les sous-commandes tempo et jaeger-query. Si vous êtes sur dd-trace, voir Vous venez de Datadog.
Entrées de statistiques base de données. Les sous-commandes pg-stat et mysql-stat lisent des exports CSV ou JSON de pg_stat_statements et de performance_schema.events_statements_summary_by_digest respectivement, avec la même auto-détection au premier octet ([ ou { signifie JSON, tout le reste CSV). Les noms de colonnes sont reconnus sans tenir compte de la casse, les colonnes timer MySQL (picosecondes) sont converties en millisecondes au parsing. Exportez la vue MySQL avec mysqlsh --result-format=csv ou tout export CSV/JSON client, SELECT ... INTO OUTFILE produit du TSV backslash-escaped non supporté.
# Export Jaeger
perf-sentinel analyze --input jaeger-export.json --ci
# Export Zipkin
perf-sentinel analyze --input zipkin-traces.json --ciMode explain
Pour débugger une trace spécifique, utilisez la sous-commande explain :
perf-sentinel explain --input traces.json --trace-id abc123-def456Cela produit une vue arborescente de la trace avec les findings annotés en ligne. Utilisez --format json pour une sortie structurée.
Export SARIF
Qu'est-ce que SARIF. Le Static Analysis Results Interchange Format est un schéma JSON standard OASIS (v2.1.0 depuis 2020) que les outils d'analyse statique utilisent pour publier leurs findings dans un format agnostique. GitHub Advanced Security et GitLab Ultimate acceptent les uploads SARIF et affichent chaque finding directement sur les pull requests, de la même manière que les résultats ESLint ou Semgrep aujourd'hui. perf-sentinel émet du SARIF pour que les findings d'anti-patterns apparaissent à côté des findings sécurité dans le même tableau de bord code scanning. Spec.
Pour l'intégration avec GitHub ou GitLab code scanning, exportez les findings en SARIF v2.1.0 :
perf-sentinel analyze --input traces.json --format sarif > results.sarifChaque finding est mappé vers un résultat SARIF avec ruleId, level, logicalLocations (service + endpoint), un tag custom properties.confidence et une valeur standard rank SARIF (0-100) dérivée de la confiance.
Champ de confiance sur les findings
Chaque finding émis en JSON ou SARIF porte un champ confidence qui indique le contexte source de la détection. Le champ est conçu pour les consommateurs en aval comme perf-lint, une intégration IDE compagnon planifiée qui ajustera la sévérité affichée dans l'IDE selon le niveau de confiance à accorder au finding. Tout outil tiers qui consomme les sorties JSON ou SARIF de perf-sentinel peut utiliser ce champ de la même manière.
Valeurs :
| Valeur | Quand émise | SARIF rank | Interprétation |
|---|---|---|---|
"ci_batch" | perf-sentinel analyze (mode batch, toujours) | 30 | Confiance faible : la trace vient d'un run CI contrôlé avec des patterns de trafic limités |
"daemon_staging" | perf-sentinel watch avec [daemon] environment = "staging" (défaut) | 60 | Confiance moyenne : patterns de trafic réels observés sur un déploiement staging |
"daemon_production" | perf-sentinel watch avec [daemon] environment = "production" | 90 | Confiance la plus élevée : trafic réel, échelle réelle, vrais utilisateurs |
Exemple de finding JSON :
{
"type": "n_plus_one_sql",
"severity": "warning",
"trace_id": "abc123",
"service": "order-svc",
"source_endpoint": "POST /api/orders/{id}/submit",
"pattern": { "template": "SELECT * FROM order_item WHERE order_id = ?", "occurrences": 6, "window_ms": 250, "distinct_params": 6 },
"suggestion": "Use WHERE ... IN (?) to batch 6 queries into one",
"first_timestamp": "2026-04-08T03:14:01.050Z",
"last_timestamp": "2026-04-08T03:14:01.300Z",
"confidence": "daemon_production"
}Fragment de résultat SARIF :
{
"ruleId": "n_plus_one_sql",
"level": "warning",
"message": { "text": "n_plus_one_sql in order-svc on POST /api/orders/{id}/submit..." },
"properties": { "confidence": "daemon_production" },
"rank": 90
}Configuration dans le daemon :
[daemon]
# "staging" (défaut) → confidence = daemon_staging, rank = 60
# "production" → confidence = daemon_production, rank = 90
environment = "production"La valeur est tamponnée sur chaque finding émis par cette instance de daemon. Les valeurs invalides (tout sauf staging/production, insensible à la casse) sont rejetées au chargement de la config avec une erreur claire. Le mode batch analyze ignore ce champ et émet toujours ci_batch.
Interopérabilité avec perf-lint (planifié). perf-lint (planifié comme intégration IDE compagnon, pas encore publié) lira le champ confidence sur les findings runtime importés et appliquera un multiplicateur de sévérité : les findings ci_batch affichés en hints, daemon_staging en warnings, daemon_production en errors. Ainsi un finding observé sur du trafic production réel remontera plus visiblement dans l'IDE qu'un finding observé uniquement dans une fixture CI.
API de requêtage du daemon
Le daemon expose une API HTTP de requêtage sur le même port que OTLP HTTP et /metrics (défaut 4318). Elle permet à des systèmes externes de récupérer les findings récents, les explications de traces, les corrélations cross-trace et la liveness du daemon sans parser les logs NDJSON. Utile pour l'alerting Prometheus, des panels Grafana custom ou des runbooks SRE.
# Liveness du daemon
curl -sS http://127.0.0.1:4318/api/status
# Findings critiques récents
curl -sS "http://127.0.0.1:4318/api/findings?severity=critical&limit=10"Voir API de query pour la référence complète par endpoint, des exemples de réponses réelles capturées, des cas d'usage (alerting Prometheus, dashboard Grafana, runbook SRE) et le contrat de stabilité.
Configuration avancée du scoring carbone
Scoring multi-région
Si vos services couvrent plusieurs régions cloud, perf-sentinel peut appliquer des coefficients d'intensité carbone par région. Le mécanisme principal est l'attribut OTel cloud.region, que la plupart des SDKs OTel cloud émettent automatiquement. Quand cet attribut est absent (ex. ingestion Jaeger/Zipkin), utilisez la table [green.service_regions] :
[green]
default_region = "eu-west-3"
[green.service_regions]
"order-svc" = "us-east-1"
"chat-svc" = "ap-southeast-1"
"auth-svc" = "eu-west-3"La chaîne de résolution est : attribut cloud.region du span > service_regions[service] > default_region > bucket synthétique "unknown". Le rapport JSON inclut un tableau regions[] trié par CO₂ décroissant.
Intégration Scaphandre (on-premise / bare metal)
Pour les serveurs on-premise ou bare metal avec support Intel RAPL, perf-sentinel peut scraper les métriques de puissance par processus de Scaphandre pour remplacer le modèle proxy par des données d'énergie mesurées.
Prérequis :
- Scaphandre installé et en cours d'exécution, exposant un endpoint Prometheus
/metrics. - Accès RAPL disponible (bare metal ou VM avec RAPL passthrough).
Configuration :
[green.scaphandre]
endpoint = "http://localhost:8080/metrics"
scrape_interval_secs = 5
process_map = { "order-svc" = "java", "game-svc" = "game", "chat-svc" = "dotnet" }Le process_map mappe les noms de service perf-sentinel au label exe dans la métrique scaph_process_power_consumption_microwatts de Scaphandre. Le daemon scrape cet endpoint toutes les scrape_interval_secs secondes et calcule un coefficient énergie-par-op par service. Les services absents du process_map retombent sur le modèle proxy. Le tag de modèle passe à "scaphandre_rapl". Seul le mode daemon watch utilise Scaphandre.
Endpoint Scaphandre authentifié
Si l'exporter Scaphandre est placé derrière un reverse proxy avec auth basic ou un ingress bearer-token, ajoutez une entrée auth_header :
[green.scaphandre]
endpoint = "https://scaphandre.mon-cluster.example/metrics"
scrape_interval_secs = 5
auth_header = "Authorization: Basic <base64>"La valeur suit le même format "Nom: Valeur" que le flag --auth-header des sous-commandes tempo et jaeger-query. Elle est marquée sensitive, hyper la masque dans les logs debug et les tables HPACK HTTP/2, et l'impl manuelle de Debug de la struct empêche toute fuite via un tracing::debug!(?config).
La variable d'environnement PERF_SENTINEL_SCAPHANDRE_AUTH_HEADER est prioritaire sur le fichier de config. Préférez la variable d'environnement en production pour éviter de committer des secrets dans le contrôle de version. Quand la valeur est définie dans le fichier de config et que la variable d'environnement est absente, un warning de démarrage oriente vers la variable d'environnement.
Envoyer un auth header en clair via http:// déclenche un tracing::warn! au démarrage du scraper, préférez https:// en production. Un header mal formé désactive le sous-système avec un tracing::error! plutôt que de réessayer en silence.
Estimation d'énergie cloud (AWS / GCP / Azure)
Pour les VMs cloud sans accès RAPL, perf-sentinel peut estimer l'énergie par service via les métriques d'utilisation CPU depuis un endpoint Prometheus et le modèle SPECpower.
Prérequis :
- Un endpoint Prometheus avec des métriques d'utilisation CPU (via cloudwatch_exporter, stackdriver-exporter, azure-metrics-exporter ou node_exporter).
- perf-sentinel n'interroge PAS les APIs des fournisseurs cloud directement.
Configuration :
[green.cloud]
prometheus_endpoint = "http://prometheus:9090"
scrape_interval_secs = 15
default_provider = "aws"
default_instance_type = "m7i.xlarge"
cpu_metric = "node_cpu_seconds_total"
[green.cloud.services.api-us]
provider = "aws"
region = "us-east-1"
instance_type = "m7i.4xlarge" # Sapphire Rapids
[green.cloud.services.analytics]
provider = "azure"
region = "westeurope"
instance_type = "Standard_D8s_v6" # Emerald RapidsLe daemon interpole la consommation avec watts = idle_watts + (max_watts - idle_watts) * (cpu% / 100) en utilisant les coefficients CCF 2026-04-24 per-vCPU embarqués (~390 types d'instances couvrant AWS, GCP, Azure, y compris les architectures modernes Sapphire Rapids, Emerald Rapids, Genoa, Turin, Graviton 3/4 et Cobalt 100). Le tag de modèle est "cloud_specpower". Fonctionnalité daemon uniquement.
Précédence des sources d'énergie. Quand plusieurs sources mesurées sont configurées pour le même service, la plus précise gagne. La chaîne complète : electricity_maps_api > alumet_rapl > scaphandre_rapl > kepler_ebpf > redfish_bmc > cloud_specpower > io_proxy_v3 > io_proxy_v2 > io_proxy_v1. Voir Limites pour la discussion sur les limites de précision de chaque source mesurée.
Endpoint Prometheus authentifié
Si votre Prometheus est derrière une auth basic, un proxy bearer-token, ou un service managé comme Grafana Cloud ou Grafana Mimir, ajoutez une entrée auth_header :
[green.cloud]
prometheus_endpoint = "https://prometheus.grafana-cloud.example/api/prom"
auth_header = "Authorization: Bearer ${GRAFANA_CLOUD_TOKEN}"La valeur suit le même format "Nom: Valeur" que le flag --auth-header des sous-commandes tempo et jaeger-query. Elle est marquée sensitive, hyper la masque dans les logs debug et les tables HPACK HTTP/2, et l'impl manuelle de Debug de la struct empêche toute fuite via un tracing::debug!(?config).
La variable d'environnement PERF_SENTINEL_CLOUD_AUTH_HEADER est prioritaire sur le fichier de config. Préférez la variable d'environnement en production pour éviter de committer des secrets dans le contrôle de version. Quand la valeur est définie dans le fichier de config et que la variable d'environnement est absente, un warning de démarrage oriente vers la variable d'environnement.
Envoyer un auth header en clair via http:// déclenche un tracing::warn! au démarrage du scraper, préférez https:// en production. Un header mal formé désactive le sous-système avec un tracing::error! plutôt que de réessayer en silence.
Calibration du modèle proxy avec des mesures terrain
Quand ni Scaphandre ni l'estimation cloud ne sont disponibles mais que vous avez des mesures d'énergie de référence (wattmètre, export RAPL, monitoring datacenter), la sous-commande perf-sentinel calibrate ajuste les coefficients I/O vers énergie du modèle proxy par service. Le workflow en trois étapes :
1. Mesurer. Exécuter une charge de référence et collecter à la fois les traces (format JSON perf-sentinel) et les mesures d'énergie (CSV avec colonnes timestamp,service,power_watts ou timestamp,service,energy_kwh, auto-détecté depuis l'en-tête).
2. Calibrer. Exécuter perf-sentinel calibrate --traces traces.json --measured-energy energy.csv --output calibration.toml. La sous-commande corrèle les ops I/O avec les lectures d'énergie par service et fenêtre temporelle, calcule factor = mesuré_par_op / proxy_par_défaut et écrit un fichier TOML. Les facteurs > 10x ou < 0.1x émettent des avertissements (probable erreur de mesure).
3. Utiliser. Charger le fichier de calibration au démarrage via [green] calibration_file = ".perf-sentinel-calibration.toml". La boucle de scoring multiplie l'énergie proxy par le facteur du service et le tag de modèle reçoit un suffixe +cal (par exemple io_proxy_v2+cal). La calibration ne s'applique qu'au modèle proxy : l'énergie mesurée Scaphandre/cloud reste prioritaire.
Intégration Tempo
Si votre infrastructure utilise Grafana Tempo comme backend de traces, vous pouvez l'interroger directement avec perf-sentinel tempo.
Workflow post-mortem. Quand une trace est plus ancienne que la fenêtre live de 30 secondes du daemon, Tempo devient la source de rejeu pour perf-sentinel tempo --trace-id …. Le workflow complet d'incident (alerte Grafana → exemplar → trace_id → rejeu) est documenté dans Runbook.
Analyse d'une trace
perf-sentinel tempo --endpoint http://tempo:3200 --trace-id abc123def456Recherche par service
# Analyser la dernière heure de traces pour order-svc
perf-sentinel tempo --endpoint http://tempo:3200 --service order-svc --lookback 1h
# Mode CI avec quality gate
perf-sentinel tempo --endpoint http://tempo:3200 --service order-svc --lookback 30m --ciPrérequis
- Tempo doit exposer son API HTTP (port 3200 par défaut).
- Le flag
--endpointpointe vers l'URL de base de l'API Tempo. - Les traces sont récupérées en protobuf OTLP et passent par le pipeline d'analyse standard.
Tempo en mode microservices (tempo-distributed)
Si votre Tempo est déployé via le chart Helm tempo-distributed et non via l'image monolithique single-binary, l'API HTTP de requête est exposée par tempo-query-frontend, pas par tempo-querier. tempo-querier est un worker interne sans API publique ; pointer --endpoint dessus renvoie HTTP 404 sur chaque /api/search. Résolvez le hostname du query-frontend comme votre environnement le permet (nom de Service Kubernetes, nom de service Docker Compose, ou hôte explicite en bare-metal) :
perf-sentinel tempo --endpoint http://tempo-query-frontend:3200 \
--service order-svc --lookback 1hUn 404 dû à un endpoint erroné remonte désormais comme Tempo returned HTTP 404 for https://.../api/search?... (l'URL qui a échoué est incluse dans le message) pour rendre la mauvaise configuration diagnosticable immédiatement.
Alternative : forwarding générique Tempo
Au lieu d'interroger Tempo, vous pouvez configurer Tempo pour qu'il forwarde une copie des traces vers perf-sentinel via son mécanisme de generic forwarding. Cela évite d'interroger Tempo et fonctionne en temps réel avec perf-sentinel watch.
Intégration API Jaeger query (Jaeger et Victoria Traces)
Si votre infrastructure utilise Jaeger upstream ou Victoria Traces comme backend de traces, les deux parlent l'API HTTP query de Jaeger et sont couverts par un seul subcommand, perf-sentinel jaeger-query. Contrairement à l'API /api/search de Tempo (ID-only), l'API /api/traces de Jaeger retourne les traces complètes en une seule requête HTTP, donc la CLI ne parallélise pas les fetches trace par trace.
Analyse d'une trace
perf-sentinel jaeger-query --endpoint http://jaeger:16686 --trace-id abc123def456Recherche par service
# Analyser la dernière heure de traces pour order-svc
perf-sentinel jaeger-query --endpoint http://jaeger:16686 --service order-svc --lookback 1h
# Même recette contre Victoria Traces (API-compatible)
perf-sentinel jaeger-query --endpoint http://victoria-traces:10428 --service order-svc --lookback 1h
# Mode CI avec quality gate
perf-sentinel jaeger-query --endpoint http://jaeger:16686 --service order-svc --lookback 30m --ciPrérequis
- Le backend doit exposer l'API HTTP de requête Jaeger (
/api/traces?service=...&lookback=...&limit=...et/api/traces/<id>). Jaeger upstream (toutes les versions récentes) et Victoria Traces sont compatibles nativement. - Le flag
--endpointpointe vers l'URL de base de l'API de requête (typiquement port 16686 pour Jaeger, port 10428 pour Victoria Traces). - Les traces sont récupérées en JSON, parsées par le même chemin
{"data": [...]}que l'ingestion Jaeger file-mode, puis passent dans le pipeline d'analyse standard. La sortie est identique àperf-sentinel analyze. --lookbackaccepte le même format1h / 30m / 2h30mque le subcommandtempo.--max-tracesmappe vers le paramètrelimitde la query backend, qui plafonne le nombre de traces retournées par recherche.
Caveats
- La lookback côté backend est bornée par la rétention configurée (Jaeger a 48h par défaut, Victoria Traces est configurable). Un
--lookbackplus large que la rétention est silencieusement tronqué à la fenêtre conservée. - Une recherche
limit=Nretourne jusqu'à N traces complètes dans un seul body HTTP. perf-sentinel plafonne la réponse à 256 MiB, ce qui couvre les workloads production typiques mais peut nécessiter un ajustement si vous recherchez régulièrement des centaines de grosses traces d'un coup. Baissez--max-tracessi vous heurtez la limite de body.--max-tracesest lui-même borné à 10 000 côté CLI. - Auth header via
--auth-header. Passez une ligne d'header au format curl ("Name: Value") pour l'attacher à chaque requête backend. Couvre Bearer tokens, Basic Auth et headers API-key custom. La valeur parsée est marquéesensitive, elle n'apparaît jamais dans les logs. Voir Limites pour les notes complètes d'usage (un seul header max par invocation, valeur visible dansps). Depuis 0.5.27, choisir la forme flag émet un événement de niveauWARNau démarrage qui oriente vers--auth-header-env <NOM>(même pattern quepg-stat), la forme variable d'environnement garde la valeur en dehors de la liste des arguments de processus et de l'historique shell. --endpointest une entrée de confiance. Le validateur rejette les schémas non-http et les URLs avec credentials, mais accepte loopback, RFC 1918 et link-local. Dans un contexte CI où la valeur de l'endpoint pourrait venir d'une PR externe, assainissez l'input en amont avant d'invoquer le subcommand.
Troubleshooting
Aucun event reçu (events_processed_total = 0)
- Vérifiez la connectivité. Depuis le container :
curl http://host.docker.internal:4318/metrics. - Vérifiez l'adresse d'écoute. perf-sentinel écoute sur
127.0.0.1par défaut. Pour l'accès Docker, configurezlisten_address = "0.0.0.0"dans.perf-sentinel.tomlou lancez-le nativement sur le host. - Vérifiez le protocole. Le Java Agent utilise gRPC par défaut (port 4317).
Events reçus mais aucun finding
- Vérifiez les attributs de span. perf-sentinel ne traite que les spans avec
db.statement/db.query.text(SQL) ouhttp.url/url.full(HTTP). - Vérifiez les seuils de détection. Le seuil N+1 par défaut est 5 occurrences du même template normalisé dans la même trace.
- Vérifiez la normalisation des URLs. perf-sentinel remplace les segments numériques par
{id}et les UUIDs par{uuid}. Les identifiants texte ne sont pas normalisés.
Erreur AOT cache avec le Java Agent
Le Java Agent (-javaagent:) est incompatible avec les AOT caches JEP 483. Désactivez le cache AOT quand l'agent est actif (voir la section Java ci-dessus).
Le starter Spring Boot ne capture pas les appels HTTP sortants
Le spring-boot-starter-opentelemetry (Spring Boot 4) fait le pont Micrometer vers OTel mais n'instrumente pas complètement les appels sortants. Utilisez le Java Agent.