// documentation

Mettre perf-sentinel en route

Un mono-binaire auto-hébergé qui lit les traces OpenTelemetry que vos services émettent déjà, repère les I/O gaspillées et les chiffre en énergie et carbone. Du premier run au quality gate CI.

Démarrage rapide

Quatre étapes, de la démo intégrée au daemon live. Rien à installer côté applications, mais vos spans doivent déjà porter le texte de la requête (db.statement) et l’URL cible (http.url) : les spans sans ces attributs sont écartés en silence, auditez donc votre tracing d’abord avec perf-sentinel inspect.

bash
# 1. Essayer la démo intégrée (rien côté apps)
perf-sentinel demo                   # rapport terminal en couleurs
perf-sentinel demo --tui             # rapport TUI interactif
perf-sentinel demo --html demo.html  # tableau de bord HTML

# 2. Analyser un fichier de traces capturées
perf-sentinel analyze --input traces.json

# 3. En quality gate CI (exit 1 si seuil dépassé)
perf-sentinel analyze --input traces.json --ci --config .perf-sentinel.toml

# 4. Streamer les traces de vos apps (mode daemon)
perf-sentinel watch

demo --html est une vitrine complète : tous les onglets du tableau de bord sont remplis (findings, Explain, GreenOps, pg_stat, Diff et corrélations inter-traces synthétisées).

Installation

Au choix : crates.io, un binaire pré-construit, ou l’image conteneur. Les binaires Linux ciblent musl : statiques, ils tournent sur n’importe quelle distribution, y compris les images FROM scratch.

bash
# depuis crates.io
cargo install perf-sentinel --locked

# ou un binaire pré-construit (Linux amd64/arm64, macOS arm64, Windows amd64)
chmod +x perf-sentinel-linux-amd64
sudo mv perf-sentinel-linux-amd64 /usr/local/bin/perf-sentinel

# ou via Docker
docker run --rm -p 4317:4317 -p 4318:4318 \
  ghcr.io/robintra/perf-sentinel:latest watch --listen-address 0.0.0.0

Un chart Helm est publié pour les déploiements Kubernetes. Les images conteneur sont disponibles sur ghcr.io et Docker Hub.

Configuration

Déposez un .perf-sentinel.toml à la racine du dépôt. Les seuils pilotent le gate CI ; la détection ajuste la façon dont les anti-patterns sont signalés.

.perf-sentinel.toml
[thresholds]
n_plus_one_sql_critical_max = 0   # tolérance zéro 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
thresholds.n_plus_one_sql_critical_max
Nombre max de N+1 SQL critiques avant échec du gate. 0 = tolérance zéro.
thresholds.io_waste_ratio_max
Part maximale d’I/O évitables tolérée, entre 0 et 1.
detection.n_plus_one_min_occurrences
Répétitions d’un template de requête dans une trace avant de compter comme N+1.
detection.slow_query_threshold_ms
Durée au-dessus de laquelle une requête SQL/HTTP est signalée comme lente.

Référence CLI

Un binaire, une poignée de sous-commandes. Lancez perf-sentinel <cmd> --help pour la liste complète des flags.

demo Jeu de données synthétique intégré, aucune capture requise.
$ perf-sentinel demo --html demo.html
analyze Analyse des traces capturées. --ci pour le gate, --tui pour le drill-down, --format json|sarif.
$ perf-sentinel analyze --input traces.json --ci
watch Daemon OTLP : ingestion gRPC :4317 / HTTP :4318, /metrics Prometheus, /health, API de query.
$ perf-sentinel watch --listen-address 0.0.0.0
report Dashboard HTML offline en un seul fichier, clair/sombre, export CSV.
$ perf-sentinel report --input traces.json --output report.html
// autres sous-commandes
diff Compare deux analyses pour détecter les régressions en CI.
explain Drill-down sur une trace (--trace-id), arbre de spans annoté.
inspect TUI autonome à quatre panneaux.
pg-stat Hotspots pg_stat_statements, classés par temps total.
calibrate Facteurs énergie per-service depuis l’énergie mesurée.
disclose Divulgation périodique vérifiable, signable via Sigstore.
tempo Récupère les traces depuis un backend Grafana Tempo.
jaeger-query Récupère depuis Jaeger ou Victoria Traces.
query Interroge le daemon live (API HTTP et TUI).
ack Gère les findings acquittés et connus.
Aide-mémoire en une ligne pour le reste de la surface
perf-sentinel explain --input traces.json --trace-id abc123        # vue arbre d’une trace
perf-sentinel inspect --input traces.json                          # TUI interactif
perf-sentinel diff --before base.json --after head.json            # diff de régression PR
perf-sentinel pg-stat --input pg_stat.csv --traces traces.json     # hotspots PostgreSQL
perf-sentinel tempo --endpoint http://tempo:3200 --trace-id <id>   # récupère depuis Grafana Tempo
perf-sentinel jaeger-query --endpoint http://jaeger:16686 --service order-svc
perf-sentinel calibrate --traces traces.json --measured-energy rapl.csv
perf-sentinel completions zsh > ~/.zfunc/_perf-sentinel            # complétions shell
perf-sentinel man > perf-sentinel.1                                # page man
perf-sentinel query findings --service order-svc                   # interroge un daemon live

Formats d’entrée et de sortie

Les traces arrivent par fichier ou en OTLP live ; les résultats sortent en texte terminal, JSON/SARIF lisibles par machine, dashboard HTML, ou flux du daemon live. La sortie est déterministe : une entrée identique produit un JSON et un SARIF byte-identiques, les findings triés sur une clé stable plutôt que sur l’ordre d’une HashMap, donc un quality gate CI ne clignote jamais.

Entrées
Fichiers de traces
JSON natif perf-sentinel, JSON Jaeger, Zipkin v2. Auto-détectés sur les premiers octets, sans flag --format.
OTLP live
gRPC :4317 et HTTP :4318, ingérés par le daemon watch depuis votre OTel Collector ou SDK.
Grafana Tempo
Récupère les traces directement depuis un backend Tempo avec perf-sentinel tempo.
API Jaeger query
Récupère depuis un backend Jaeger ou Victoria Traces avec perf-sentinel jaeger-query.
pg_stat_statements
Classe les hotspots PostgreSQL depuis la vue catalogue avec perf-sentinel pg-stat.
Sorties
text (défaut)
Sortie terminal colorée, regroupée par sévérité.
json
Rapport structuré au schéma documenté.
sarif (v2.1.0)
Code scanning GitHub/GitLab avec annotations PR inline.
Dashboard HTML
Rapport offline en un seul fichier avec export CSV.
TUI interactif
Analyze, Inspect et Explain en un seul drill-down clavier.
Daemon live
Findings NDJSON, /metrics Prometheus, sonde /health, API de query.
Divulgation périodique
JSON perf-sentinel-report/v1.0 vérifiable par hash, signable Sigstore.

Métriques GreenOps

Chaque finding porte une lecture GreenOps. Les chiffres sont directionnels : ils servent à classer et suivre une tendance, pas à certifier (pour le moment).

Score d’intensité I/O (IIS) ops I/O ÷ invocations
Total des opérations I/O d’un endpoint divisé par son nombre d’invocations. Plus il est élevé, plus chaque appel coûte en I/O.
Ratio de gaspillage I/O évitables ÷ totales
Opérations évitables sur opérations totales : la part directement récupérable en corrigeant les N+1 et les appels redondants.
co2.total (E × I) + M
Reporté comme le numérateur Software Carbon Intensity v1.0 (ISO/IEC 21031:2024) : énergie consommée (E) × intensité carbone du réseau (I), plus émissions matérielles embarquées (M), sommé sur les traces analysées.
Bandes (stables entre versions)
healthy moderate high critical
Les valeurs d’enum io_intensity_band et io_waste_ratio_band sont stables entre versions ; les seuils numériques sous-jacents peuvent évoluer.

Le volet carbone chiffre les I/O détectées avec la rigueur d’un calculateur carbone spécialisé émissions logicielles et compute : méthodologie activity-based, intensité grid horaire par région (Electricity Maps, ENTSO-E, RTE, National Grid ESO, EIA, et plus), carbone embarqué bottom-up (Boavizta et HotCarbon 2024) et disclosures signées Sigstore vérifiables par hash.

Il convient comme source primaire de données pour une plateforme de comptabilité carbone horizontale, ou comme outil de contrôle interne pour les KPI d’émissions logicielles et la conformité RGESN.

Il n’est pas encore vérifié par tiers-partie pour un reporting CSRD ou GHG Protocol Scope 2/3 standalone, qui exige un audit par un organisme qualifié et l’intégration des scopes non-IT. Les chiffres CO₂ portent un encadrement ~2× en mode proxy par défaut, plus serré avec une source d’énergie mesurée (Scaphandre RAPL, Kepler eBPF, Redfish BMC ou SPECpower cloud avec calibration). Sources et bornes : la précision des estimations et la méthodologie.

Couplages concrets : passer les comptes I/O et estimations énergie par région à Watershed, Sweep, Greenly ou Persefoni comme activity data, ou utiliser perf-sentinel directement pour démontrer la conformité RGESN (Référentiel Général d’Écoconception de Services Numériques, ARCEP/Ademe/DINUM 2024) sur les critères d’optimisation logicielle, où détection de N+1, appels redondants, caching et réduction du fanout correspondent aux critères concernés.

Pour les organisations qui souhaitent malgré tout publier une disclosure périodique non-réglementaire d’efficacité logicielle (JSON trimestriel ou annuel, signature Sigstore optionnelle, volontairement écartée du chemin de démarrage principal), le workflow optionnel perf-sentinel disclose est documenté dans le guide de divulgation.

Performance

perf-sentinel bench ne chronomètre que le pipeline d’analyse (normalize → correlate → detect → score), mono-thread, sur jeux de données synthétiques : le coût pur du pipeline, pas un débit de bout en bout ni un benchmark du daemon sous charge.

Jeu de données (44 043 évènements synthétiques) Plateforme Débit pipeline p50 / p99 par évènement
Motif répété x86 Xeon 8481C ~576k évt/s 1,72 / 1,88 µs
Motif répété Apple M4 Pro ~1,23M évt/s 0,81 / 0,89 µs
SQL varié x86 Xeon 8481C ~640k évt/s 1,54 / 1,69 µs
SQL varié Apple M4 Pro ~1,33M évt/s 0,75 / 0,81 µs
  • x86 : GCP c3-standard-8 (Xeon 8481C à 2,70 GHz, 8 vCPU), mesuré en juin 2026.
  • M4 : Mac mini M4 Pro (12 cœurs, 24 Go), mesuré le 2026-06-08.

Tous deux en release 0.8.5 (musl+mimalloc sur x86, allocateur système en natif sur M4). Avec les artefacts natifs, le M4 Pro tient environ 2,1× un vCPU 8481C (2,14× répété, 2,08× varié). p50 / p99 = latence par évènement sur 10 itérations. Mémoire du daemon : ~17 Mo RSS au repos, pic ~190 Mo sous une charge d’ingestion soutenue de ~1,0M évt/s (contre 237 Mo sur 0.6.1, sous le plafond de 250 Mo). Rust édition 2024, rustc 1.96.0. Reproduire avec perf-sentinel bench --help.

Méthodologie du bench (périmètre du chrono, jeux de données)

La lecture du fichier, le parsing JSON et l’ingestion ont tous lieu avant le démarrage du chrono, et les lots d’entrée sont clonés en amont. Le pipeline est mono-thread (pas de rayon), le nombre de cœurs ne change donc pas le débit. Les deux jeux comptent 44 043 évènements synthétiques, construits en dupliquant la fixture de démo, l’un répète le même motif, l’autre du SQL aléatoire par requête. Cela isole le débit du pipeline mais ne reflète pas la diversité d’une vraie production.

Détail par allocateur sur la même puce

L’artefact musl x86 est lié à mimalloc tandis que l’artefact macOS arm64 utilise l’allocateur système ; les binaires diffèrent donc par l’allocateur autant que par l’ISA. Sur le même M4 Pro, le build musl+mimalloc (l’artefact linux/arm64 en conteneur Docker) atteint ~1,39M / ~1,51M évt/s contre ~1,23M / ~1,33M en natif, ~13 % plus vite que l’allocateur natif de macOS, confirmant l’allocateur comme cause principale du débit plus élevé en Docker. À build équivalent, le M4 Pro fait ~2,4× le x86 8481C (2,41× répété, 2,36× varié).

Mémoire : rss_peak du bench vs empreinte du daemon

bench affiche rss_peak_bytes, mais cette valeur est dominée par les lots d’entrée pré-clonés (10 itérations × 44 043 évènements), ce n’est pas l’empreinte du daemon. Ce rss_peak n’est pas non plus comparable entre OS (RSS courant via /proc sous Linux, pic via getrusage sous macOS). Profilé séparément sur le même M4 Pro dans une VM Docker Desktop linux/arm64, le daemon tourne à ~17 Mo au repos en musl+mimalloc (~10 Mo en natif, mimalloc échange un peu de RSS contre de la vitesse) et culmine à ~190 Mo sous une charge soutenue de ~1,0M évt/s, contre 237 Mo sur 0.6.1, sous le plafond de 250 Mo.

Comment ça se compare

La niche de perf-sentinel : léger, agnostique du protocole, natif CI/CD et carbon-aware, pas un remplacement d’une suite d’observabilité complète.

Capacité Hypersistence Datadog New Relic Sentry Digma Pyroscope OTJAE perf-sentinel
Détection N+1 SQL JPA uniquement, test-time Oui (DBM) Oui Oui (OOTB) Oui, IDE (JVM/.NET) Non Non Oui, niveau protocole
Détection N+1 HTTP Non Oui Oui Oui Partiel Non Non Oui
Support polyglotte Java uniquement Agents par langage Agents par langage Plupart des langages (SDK) JVM + .NET eBPF + SDKs JVM uniquement Tout runtime OTel
Corrélation cross-service Non Oui Oui Oui Limité Trace-to-profile Intra-JVM uniquement Via trace ID
Carbone/énergie par span Non Non Non Non Non Non Oui (CCF) Oui (SCI, directionnel)
Score GreenOps (IIS, gaspillage) Non Non Non Non Non Non Non Intégré
Empreinte runtime Bibliothèque Agent ~100–150 Mo Agent ~100–150 Mo SDK + backend Backend local Agent ~50–100 Mo Agent JVM Autonome <20 Mo
Quality gate CI/CD natif Asserts manuels Alertes, pas de gate Alertes, pas de gate Alertes, pas de gate Non Non Non Oui (exit 1)
Licence Commerciale SaaS propriétaire SaaS propriétaire FSL → Apache-2 Freemium AGPL-3.0 Apache-2.0 AGPL-3.0

Les empreintes d’agent des APM commerciaux sont des estimations d’ordre de grandeur issues de déploiements publics ; l’overhead réel dépend du périmètre d’instrumentation.

Acquittement de findings connus

Déposez un .perf-sentinel-acknowledgments.toml à la racine du dépôt pour supprimer les findings que l’équipe a acceptés : chacun reçoit une signature SHA-256, et ils sont filtrés de analyze / report / inspect / diff et ne comptent pas dans le quality gate. Les acks runtime contre un daemon live sont exposés via le CLI ack, le dashboard HTML live et le TUI.

Flux d’acquittement

Déploiement

Un binaire, deux modes, quatre environnements, trois modèles de déploiement. Choisissez le plus léger adapté à l’endroit où vivent vos traces. Le collecteur central est un daemon unique avec état, donc des réplicas horizontaux exigent un load balancing par trace-id et ne partagent pas l’état de corrélation. L’échantillonnage de traces en amont (head ou tail) et le sampling_rate du daemon sous-comptent les détecteurs par répétition, et sous surcharge soutenue le daemon déleste des lots d’analyse entiers plutôt que de bloquer l’ingestion, chaque délestage étant compté dans les métriques, jamais une perte silencieuse.

Autonome
Analyse locale, TUI et rapports HTML en un seul fichier. Rien à déployer côté apps.
Batch CI
perf-sentinel analyze --ci sur traces capturées, exit 1 au dépassement de seuil. Le gate le plus simple, rien de long-running.
Daemon sidecar
Un daemon watch par service, qui ingère les traces OTLP de ce service juste à côté, pour du debug isolé.
Collector central
Un OpenTelemetry Collector route vers un daemon watch unique long-running, avec /metrics Prometheus et l’API de query.
Dev local
CI/CD
Staging
Production
GreenOps (transversal)
Vue d’ensemble

Moniteur opérateur live sur un daemon en marche, pour les DevOps / SRE, quatre onglets cyclés par Tab (hints Advisor, mix énergie/carbone, courbes Trends, santé des Scrapers) via perf-sentinel query --daemon <URL> monitor :

Guide d’intégration complet

Confidentialité & sécurité

perf-sentinel est conçu pour rester dans votre périmètre et discret.

Traitement sur place
Les traces sont analysées localement. Aucun appel réseau sortant silencieux, aucune télémétrie d’usage embarquée. Le contenu brut des spans reste en mémoire uniquement (TTL 30s, LRU de 10k traces) et n’est jamais écrit sur disque ; tout ce qui est émis ne porte que le template normalisé, valeurs SQL et URL remplacées par des placeholders.
Localhost par défaut
Le daemon écoute sur 127.0.0.1. TLS, CORS et la clé d’API d’acquittement sont tous opt-in.
Surface verrouillable
Les endpoints GET en lecture seule et les listeners d’ingestion OTLP peuvent être restreints ; le modèle de menace sans auth est documenté. Les endpoints font confiance à leurs émetteurs, gardez donc l’ingestion sur un réseau de confiance et placez un reverse proxy ou une network policy devant avant toute exposition au-delà de localhost.
Limites & modèle de menace

Chaîne d’appro. & releases

Chaque GitHub Action est épinglée à un SHA de commit de 40 caractères, l’image de production est FROM scratch, Cargo.lock est commité et audité quotidiennement par cargo audit, et les permissions de token des workflows sont en lecture seule par défaut. Les releases sont signées avec Sigstore et portent une provenance SLSA.

Les publications suivent une procédure documentée, gardée par un run obligatoire du lab de simulation, le chart Helm étant versionné en lockstep. Les binaires de release portent une provenance SLSA Build L3 (Sigstore + Rekor) et des données cargo-auditable embarquées (cargo audit bin), et chaque release publie un SBOM SPDX attesté sous le prédicat SPDX. Dependabot ouvre des PR hebdomadaires groupées.

Chaîne d’appro. & provenance

Documentation détaillée

Chaque sujet ci-dessus est couvert en intégralité dans la référence. Accès direct à une page :

perf sentinelperf sentinel docs
GitHub
{{ docKicker }}

{{ docTitle }}

{{ docLead }}

{{ qsTitle }}

{{ qsIntro }}

bash
{{ qsCode }}

{{ qsNote }}

{{ inTitle }}

{{ inIntro }}

bash
{{ inCode }}

{{ inNote }}

{{ cfTitle }}

{{ cfIntro }}

.perf-sentinel.toml
{{ cfCode }}
{{ f.key }}
{{ f.desc }}

{{ cliTitle }}

{{ cliIntro }}

{{ c.name }} {{ c.desc }}
$ {{ c.exampleEl }}
{{ cliMoreLabel }}
{{ c.name }} {{ c.desc }}
{{ cliCheat }}
{{ csCodeEl }}

{{ fmTitle }}

{{ fmIntro }}

{{ fmInLabel }}
{{ f.name }}
{{ f.desc }}
{{ fmOutLabel }}
{{ f.name }}
{{ f.desc }}

{{ mtTitle }}

{{ mtIntro }}

{{ m.name }} {{ m.formula }}
{{ m.desc }}
{{ mtBandsLabel }}
{{ b.name }}
{{ mtBandsNote }}

{{ mtRigor }}

{{ mtSuitable }}

{{ mtNotVerified }} {{ mtLimitsText }} {{ mtAnd }} {{ mtMethodText }}.

{{ mtPairings }}

{{ mtDisclose }} {{ mtReportText }}.

{{ pfTitle }}

{{ pfIntro }}

{{ h }}
{{ r.ds }} {{ r.plat }} {{ r.thr }} {{ r.lat }}
  • {{ pv }}

{{ pfNote }}

{{ d.title }}

{{ d.body }}

{{ cpTitle }}

{{ cpIntro }}

{{ t }}
{{ r.cap }} {{ c }}

{{ cpFoot }}

{{ akTitle }}

{{ akIntro }}

{{ akMore }}

{{ dpTitle }}

{{ dpIntro }}

{{ d.name }}
{{ d.desc }}
{{ d.label }}

{{ dpMonitorCap }}

{{ dpMore }}

{{ pvTitle }}

{{ pvIntro }}

{{ p.name }}
{{ p.desc }}
{{ pvMore }}

{{ spTitle }}

{{ spIntro }}

{{ spRelease }}

{{ spMore }}

{{ frTitle }}

{{ frIntro }}

{{ lightboxEl }}