What's new in v0.5.14
v0.5.13 promised in the report --help documentation that --input honored "Same format auto-detection as analyze --input (native JSON, Jaeger, Zipkin v2)". The implementation only delivered for native JSON and Zipkin v2. A Jaeger JSON export ({"data": [{"spans": [...]}]}) was misrouted to the Report-JSON parser and died with Error parsing --input as Report JSON: missing field 'analysis'. The shared helper load_report_from_input (consumed by both report and inspect) dispatched purely on the first non-whitespace byte, so any top-level { was treated as a pre-computed Report. Native event arrays and Zipkin v2 inputs (top-level [) were already accepted via JsonIngest::detect_format. Only Jaeger was broken. v0.5.14 closes that gap with a small CLI-side dispatch fix, the daemon and the scoring pipeline are untouched.
The { branch now tries serde_json::from_slice::<Report> first (the daemon snapshot fast path, succeeded short-circuits before any ingest). On parse failure the helper hands off to JsonIngest::ingest, which routes Jaeger via the existing detect_format heuristic. The trade-off is one extra full Report parse on Jaeger inputs, negligible in practice because the canonical Jaeger ingestion path is tempo / jaeger-query / analyze. The daemon snapshot pipeline curl /api/export/report | perf-sentinel report --input - keeps a single parse and identical performance to v0.5.13.
The pre-existing MAX_JSON_DEPTH cap on the Report fast path is now an explicit guard with its own dedicated error message (--input JSON exceeds maximum nesting depth of 32) rather than a silent fallthrough into the ingest fallback, so an over-deep payload exits with the right diagnostic instead of a misleading downstream error.
The error surfaced on unrecognized top-level objects is also clearer. When the input is neither a Report JSON nor a Jaeger export, report and inspect now print Error: --input top-level object is neither a pre-computed Report JSON nor a Jaeger export. Underlying error: ... instead of the misleading missing field 'analysis' text that hid the real disambiguation. Operators reading a CI log should now see what was expected without guessing.
The fix benefits inspect --input symmetrically because both subcommands consume the same helper. The docs/ci-templates/gitlab-ci.yml template's commented-out Pages section, which feeds report --input with a raw Jaeger export, becomes effectively functional with no template change required.
Changed
report --inputandinspect --inputnow accept Jaeger JSON exports as documented. The shared helperload_report_from_inputpreviously routed any top-level{straight to the Report-JSON parser, killing Jaeger inputs withmissing field 'analysis'. The new dispatch tries Report first, falls back toJsonIngest(Jaeger viadetect_format) on parse failure. Workaround pipelines likecat traces.jaeger.json | perf-sentinel analyze --format json | perf-sentinel report --input -are no longer needed.- Clearer error on unrecognized top-level objects. A bogus
{-rooted input that is neither a Report JSON nor a Jaeger export now printsError: --input top-level object is neither a pre-computed Report JSON nor a Jaeger export. Underlying error: ..., with the underlying serde / ingest message preserved on a second line. - Explicit
MAX_JSON_DEPTHguard on the Report fast path. An over-deep payload now exits with--input JSON exceeds maximum nesting depth of 32instead of silently falling through to the ingest fallback and producing a less-targeted error. - Doc-comment of
Commands::Reportextended with one sentence acknowledging the Report JSON snapshot path. The advertised auto-detection contract on Jaeger, Zipkin, native is unchanged because it was always the intended behavior, only the implementation rejoined the contract.
Behavior
- Daemon snapshot pipelines unchanged.
curl /api/export/report | perf-sentinel report --input -keeps the fast path. A successfulserde_json::from_slice::<Report>short-circuits before the ingest fallback, so there is no extra cost on the most common path. green_summaryaudit-trail fields flow through verbatim on the snapshot fast path.top_offenders,regions,transport_gco2,co2, and thescoring_configblock introduced in v0.5.12 are deserialized straight from the daemon snapshot and rendered as-is, no re-scoring. A new test (cli_report_accepts_report_snapshot_input) pins the contract on a populated fixture.- Native event arrays and Zipkin v2 inputs unchanged. They were already routed correctly via the top-level
[branch andJsonIngest::detect_format. The dispatch refactor preserves that behavior with regression-guard tests. - No SARIF, JSON, terminal or HTML output format change. No daemon-side code touched. The fix is internal to the CLI helper, consumed by
reportandinspectonly.
Install
Prebuilt binaries (Linux amd64 / arm64, macOS arm64, Windows amd64):
curl -LO https://github.com/robintra/perf-sentinel/releases/download/v0.5.14/perf-sentinel-linux-amd64
chmod +x perf-sentinel-linux-amd64
sudo mv perf-sentinel-linux-amd64 /usr/local/bin/perf-sentinelLinux binaries are statically linked against musl and run on any distribution (Alpine, Debian, RHEL, Ubuntu any version) regardless of glibc version, and inside FROM scratch images.
From crates.io:
cargo install perf-sentinel --version 0.5.14Docker:
docker run --rm -p 4317:4317 -p 4318:4318 \
ghcr.io/robintra/perf-sentinel:0.5.14 watch --listen-address 0.0.0.0Also available on Docker Hub: robintrassard/perf-sentinel:0.5.14.
Helm (chart 0.2.17 ships 0.5.14 as its appVersion default):
helm install perf-sentinel oci://ghcr.io/robintra/charts/perf-sentinel \
--version 0.2.17 \
--namespace observability --create-namespaceVerify the binary against SHA256SUMS.txt:
curl -LO https://github.com/robintra/perf-sentinel/releases/download/v0.5.14/SHA256SUMS.txt
sha256sum -c SHA256SUMS.txt --ignore-missingFull diff: v0.5.13...v0.5.14