What's new in v0.5.6
Framework-aware suggestions now fire on Spring Boot + Spring Data JPA stacks. v0.5.5 shipped the code.* parent walker to recover attributes from ancestor spans, but suggested_fix still came back null on every JPA finding because the walker would stop on the user's Spring Data repository span where code.namespace is the user's class (e.g. com.example.OrderRepository), not a framework package. v0.5.6 closes the gap with two complementary signals: the OpenTelemetry instrumentation scope chain (io.opentelemetry.spring-data-3.0, io.opentelemetry.hibernate-6.0, etc.) captured at ingest, and the canonical Spring Data naming convention (*Repository, *Repo, *Dao) recognised on the user class itself. Either signal alone is enough to surface the JPA-specific suggested_fix; together they cover the realistic spectrum of how the OTel Java agent attributes user code to a framework.
After upgrading, a Spring Boot + JPA app instrumented with the default OTel Java agent produces findings with a populated framework: "java_jpa" and an actionable recommendation (JOIN FETCH / @EntityGraph for n_plus_one_sql, @Cacheable / first-level cache for redundant_sql). The same machinery applies to Quarkus reactive (io.opentelemetry.hibernate-reactive-1.0), Quarkus non-reactive (io.opentelemetry.quarkus-resteasy-reactive-3.0), Spring WebFlux (io.opentelemetry.spring-webflux-5.0, io.opentelemetry.r2dbc-1.0), so framework detection generalises beyond JPA.
Added
- OpenTelemetry instrumentation scope chain captured per
SpanEvent. New optionalinstrumentation_scopes: Vec<String>field onSpanEventandFinding. Populated at OTLP ingest by walking the parent chain (bounded by 8 hops) and collecting each uniquescopeSpans[].scope.nameleaf to root. Empty on Jaeger / Zipkin / native JSON ingest where no scope info is carried. Skipped from JSON serialisation when empty, so the wire format stays backwards-compatible with v0.5.5 consumers. - Scope-based framework detection in
detect_framework. NewSCOPE_RULEStable maps OTel scope short-names to internal frameworks:spring-dataandhibernateto JPA,hibernate-reactiveto Quarkus reactive,quarkusto Quarkus non-reactive,spring-webfluxandr2dbcto WebFlux. Order matters (reactive variants ranked above the catch-all hibernate rule). The scope check runs BEFORE the namespace heuristics, so it dominates whenever the agent emits scope info. Boundary-aware match requires theio.opentelemetry.prefix and a segment boundary (-or end of string) after the needle, which excludes third-party tracers that happen to contain a framework name in their identifier (com.acme.quarkus-monitoringno longer matches thequarkusrule). - User-code naming convention hints in
JAVA_RULES. Three newLastSegmentEndsWithpatterns (Repository,Repo,Dao) in the JPA rule. Catches Spring Data repositories whosecode.namespaceiscom.example.OrderRepositoryrather than a Spring framework package. Existing framework-package substrings (org.springframework.data.jpa,org.hibernate, etc.) keep priority and still match first. - Namespace-only fallback when
code.filepathis absent. OTel agents often emitcode.namespaceon a parent span withoutcode.filepath. v0.5.5 returnedNonefrom the detector in that case, so no fix attached. v0.5.6 falls back to iterating every language's rules in order (Java, C#, Rust) when the filepath is missing, returning the first match. No language-generic fallback fires here because we cannot pick one without a filepath signal. (RedundantSql, JavaJpa)mapping in theFIXEStable. Was missing in v0.5.5 even though detection could identify JPA: the lab finding type isredundant_sql, so the lookup returnedNone. The new entry recommends@Cacheableon the repository or service method, or sharing theEntityManagerwithin the request via@Transactionalso Hibernate's first-level cache deduplicates the read. Reference URL points at the Spring Cache abstraction docs.
Fixed
- Cross-trace slow findings now carry
code_locationandinstrumentation_scopesfrom the worst trace.detect_slow_cross_tracepreviously emitted these fields asNone/ empty even when a representative span existed in the entries. The internal entry tuple now carries&SpanEventso the framework-detection path runs on cross-trace findings too, matching the per-trace detectors' contract. JPA, Hibernate and friends are now recognised on cross-trace slow findings.
Security
- Sanitize
instrumentation_scopesat the ingest boundary. Newsanitize_string_vechelper drops elements with ASCII control characters, truncates each scope to 256 bytes, and caps the Vec at 8 elements. Closes the regression introduced when the field landed without going through the canonicalsanitize_span_eventpath: an attacker-supplied OTLP payload could otherwise stuff a 1 MB control-char-laden scope name into every event, amplified again on eachFinding.instrumentation_scopes.clone(). The cap matches the OTLP parent-walk bound and also fires on the JSON ingest path which has no structural depth bound.
Changed
- Single-pass attribute classifier in
convert_span. Replaces ~14 separate linear scans over the OTLP attribute list with one pass that fills aClassifiedAttrs<'_>struct viamatchon the key. ~13x fewer key comparisons per span at typical 30-attribute HTTP spans, with no allocation regression. - Helm chart 0.2.9 bumps
appVersionand the default daemon image tag to0.5.6. Drop the workaroundcompression: noneon the OTel Collector now that gzip is supported natively (already true since v0.5.5, this release just consolidates the framework detection on top).
Install
Prebuilt binaries (Linux amd64 / arm64, macOS arm64, Windows amd64):
curl -LO https://github.com/robintra/perf-sentinel/releases/download/v0.5.6/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-sentinelDocker:
docker run --rm -p 4317:4317 -p 4318:4318 \
ghcr.io/robintra/perf-sentinel:0.5.6 watch --listen-address 0.0.0.0Also available on Docker Hub: robintrassard/perf-sentinel:0.5.6.
Helm (chart 0.2.9 ships 0.5.6 as its appVersion default):
helm install perf-sentinel oci://ghcr.io/robintra/charts/perf-sentinel \
--version 0.2.9 \
--namespace observability --create-namespaceVerify the binary against SHA256SUMS.txt:
curl -LO https://github.com/robintra/perf-sentinel/releases/download/v0.5.6/SHA256SUMS.txt
sha256sum -c SHA256SUMS.txt --ignore-missingFull diff: v0.5.5...v0.5.6