Post-review fixes for the SpanGuard scope-leak change:
- SpanGuard.h: restore the injectCurrentContextToProtobuf real-class
declaration, its #else no-op stub, and the protocol::TraceContext
forward-declaration dropped during the 3->4 union merge (compile break).
- Re-parent consensus.phase.open and consensus.establish under the round
span via a new RCLConsensus::Adaptor::roundSpanContext() accessor: the
round span is now detached, so ambient parenting no longer works; the
phase spans link explicitly to the captured round context. csf::Peer
gains a matching no-op accessor so the generic engine still compiles.
- Switch five childSpan(op::X, ctx) sites to the full consensus::span::X
constants: childSpan(name, ctx) takes the name verbatim, so the suffix-
only op:: constants emitted short, non-dotted span names.
- TestTelemetry mock: add getConsensusTraceStrategy() override (base pure
virtual introduced on this branch) so the mock is not abstract.
- validate(): rewrite the trace_context comment to drop attack-surface
framing and the PR-discussion reference.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The otel-naming Rule F check scans @code doc examples as call sites; raw
string literals there trip the rule. Reference *SpanNames.h constants /
placeholder identifiers in the examples so comments follow the naming rules.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
detached() strips the thread-local OTel Scope so a guard can be safely
moved to and destroyed on another thread; it pops the Scope on the origin
thread and moves the span into a scope-less guard. rootSpan() starts a span
as a fresh trace root (kIsRootSpanKey) so inbound entry points never inherit
an ambient span left active on the thread.
Impl now holds an optional<Scope> (nullopt for detached guards). Updated the
SpanGuard class docs and docs/build/telemetry.md with the cross-thread rules.
The unit test lands on phase2 where the telemetry test module exists.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Remove unused SpanNames.h from Transactor.cpp (transitively included
via TxApplySpanNames.h). Remove unused <ranges> from
TraceContextValidation.h (std::ranges::any_of lives in <algorithm>).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add missing <cstdint> include for std::uint32_t in Telemetry.h.
Add braces around single-line if bodies in Telemetry.cpp.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Give developers direct access to the full OpenTelemetry metrics API,
symmetric with getTracer(), so all seven OTel instrument types
(including UpDownCounter, sync Gauge, and the observable variants) are
reachable — not just the four beast::insight models.
- Telemetry: build and own the metrics pipeline (OTLP HTTP metric
exporter + PeriodicExportingMetricReader + SpanMetrics histogram view)
alongside the tracer, sharing the same resource attributes and TLS
config. Register it globally via metrics::Provider::SetMeterProvider
and expose Telemetry::getMeter(). Metrics enable with [telemetry];
the metrics endpoint is derived from the trace endpoint.
- beast OTelCollector: no longer owns a pipeline. It fetches the global
Meter, becoming a thin shim over the shared provider (legacy path
during beast deprecation). This also resolves the review note that the
metric exporter ignored [telemetry] use_tls — TLS now comes from the
shared telemetry pipeline.
- Add a libxrpl unit test covering getMeter() on the enabled (global
provider) and disabled (noop) paths, exercising an UpDownCounter.
Design: docs/superpowers/specs/2026-07-07-direct-otel-metrics-api-design.md
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Soften the "non-reversible" wording in Redaction.h. XRP account
addresses are a public, enumerable set, so an unsalted hash is
obfuscation (keeps plaintext out of traces/dashboards), not a
secrecy guarantee. Document why a salt is intentionally omitted:
it would break cross-node/restart correlation, the reason for
hashing rather than dropping the attribute.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The native OTel metrics path hard-coded service.name="xrpld" and stamped
no network attribute, while traces stamped a configurable service.name
and xrpl.network.type. Metrics therefore could not be filtered by service
or network. Align the two paths:
- OTelCollector::New / OTelCollectorImp gain serviceName + networkType
params. service.name uses the configured value (default "xrpld" when
unset, preserving today's behavior); xrpl.network.type is stamped when
provided. The key is a string literal because beast/insight sits below
the telemetry module and cannot include its SpanNames const.
- CollectorManager reads service_name from [insight], falling back to the
[telemetry] value, and receives the network type from the caller.
- Application derives the network type once via the shared
telemetry::networkTypeFromId, now declared in Telemetry.h and moved out
of an anonymous namespace so the trace and metric paths reuse a single
0/1/2 -> mainnet/testnet/devnet mapping (no duplication).
Dashboards (5 system-* files): add $service_name, $deployment_environment,
$xrpl_network_type template variables and wire them into every panel query
that filters by $node.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>