Commit Graph

73 Commits

Author SHA1 Message Date
Pratik Mankawde
6a9eaedeac Merge branch 'pratik/otel-phase1b-telemetry-infra' into pratik/otel-phase1c-rpc-integration
Brings coroutine-aware OTel context storage forward: CoroAwareContextStorage,
its install in Telemetry, ScopedSpanGuard same-store assertion, and the
non-owning ScopedActivation helper.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:49:26 +01:00
Pratik Mankawde
e3c724423f feat(telemetry): add ScopedActivation for non-owning span activation
Add SpanGuard::activate() returning a ScopedActivation RAII helper that
activates an already-owned span (from a thread-free SpanGuard) as the
current context WITHOUT taking ownership. The activation pushes the span
onto the current LocalValue context store on construction and pops it on
destruction; it never ends the span (its owning SpanGuard does).

This lets a job-handoff span be made ambient for the duration of a
synchronous, non-yielding worker body so log lines there carry the
span's trace_id. Non-copyable and non-movable, mirroring ScopedSpanGuard.

owner is captured after the Scope push via declaration-order member
initialization (scope declared before owner), matching the A3
capture-after-materialization invariant. A #else no-op stub keeps the
API zero-overhead when telemetry is compiled out.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:41:02 +01:00
Pratik Mankawde
7dbbf95c72 refactor(telemetry): ScopedSpanGuard asserts same context store, not thread
Coroutine-aware storage lets a scope resume on another worker within the same
coroutine store; store-identity is the correct pop-safety invariant. Same-store
equals same-thread for synchronous code, so no safety is lost.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 18:52:48 +01:00
Pratik Mankawde
9fa58067fa feat(telemetry): install coroutine-aware context storage at start
Install CoroAwareContextStorage as the process-wide OTel runtime context
storage in TelemetryImpl::start(), before SetTracerProvider and before any
span is created, so the ambient context follows JobQueue coroutines across
yield/resume. Hold it in a member for the process lifetime; not reset in
stop() because resetting while spans may exist is SDK undefined behaviour.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 17:12:11 +01:00
Pratik Mankawde
f1b7104bb8 feat(telemetry): coroutine-aware OTel context storage
Backs the OTel active-context stack with xrpl::LocalValue so the ambient
context follows a JobQueue::Coro across yield/resume. Not yet installed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 16:56:16 +01:00
Pratik Mankawde
7fec5d75d5 Merge branch 'pratik/otel-phase1b-telemetry-infra' into pratik/otel-phase1c-rpc-integration 2026-07-22 16:20:28 +01:00
Pratik Mankawde
d865a266b5 fix(telemetry): clang-tidy naming + include-cleaner in DeterministicIdGenerator
- Rename file-local thread_local globals to the .clang-tidy convention
  (GlobalVariablePrefix "g" + CamelCase), keeping the Tls marker:
  tlsPendingTraceId -> gTlsPendingTraceId, tlsPendingConsumed -> gTlsPendingConsumed.
- Add direct includes for opentelemetry trace_id.h / span_id.h (header uses
  TraceId/SpanId in signatures) and sdk/trace/id_generator.h (.cpp references
  IdGenerator directly) to satisfy misc-include-cleaner.

Both verified clean with clang-tidy against a telemetry-enabled compile DB.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 16:20:16 +01:00
Pratik Mankawde
c9fb16e67b Merge branch 'pratik/otel-phase1b-telemetry-infra' into pratik/otel-phase1c-rpc-integration
# Conflicts:
#	include/xrpl/telemetry/SpanGuard.h
2026-07-21 21:53:54 +01:00
Pratik Mankawde
0e7ca13cf3 refactor(telemetry): split context capture into spanContext() (own span) + threadLocalContext() (current)
OTel distinguishes a span's own context from the thread's
current/ambient context; SpanGuard is unscoped so spanContext()
(own span, default) is what cross-thread childSpan parenting needs,
and threadLocalContext() (static) snapshots RuntimeContext::GetCurrent()
for propagation. Renames captureContext.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 21:48:40 +01:00
Pratik Mankawde
ec5cd97de6 Merge branch 'pratik/otel-phase1b-telemetry-infra' into pratik/otel-phase1c-rpc-integration 2026-07-21 21:07:24 +01:00
Pratik Mankawde
97e6586f71 fix(telemetry): captureContext() captures the guard's own span, not ambient
SpanGuard is unscoped (never pushed onto the thread-local context stack), so
RuntimeContext::GetCurrent() returned an unrelated ambient span. Build the
captured context from impl_->span so captureContext() is correct for every
caller regardless of scoping — childSpan(name, ctx) then parents explicitly to
this span across threads. Matches getTraceBytes()'s own-context behavior.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 21:07:20 +01:00
Pratik Mankawde
d2d114595b Merge branch 'pratik/otel-phase1b-telemetry-infra' into pratik/otel-phase1c-rpc-integration 2026-07-21 18:43:26 +01:00
Pratik Mankawde
4d95f455f4 feat(telemetry): add DeterministicIdGenerator + PendingTraceId for true-root deterministic trace_ids
Add a custom OTel IdGenerator that returns a thread-local pending trace_id on
the SDK no-parent (root) branch and a random one otherwise, plus a PendingTraceId
RAII guard that pins that id for the next forced-root span and asserts on
destruction that it was consumed. Wire the generator into
TracerProviderFactory::Create via its 4-arg overload.

This lets hash-derived spans become true trace roots so they line up into one
trace across nodes. It is installed but dormant on this branch: the caller
(hashSpan) arrives on a later branch (phase-3). GenerateSpanId is always random
and is_random_ is false so the W3C random-trace-id flag is not set on
deterministic ids.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 18:42:54 +01:00
Pratik Mankawde
508c91d36c refactor(telemetry): split SpanGuard into unscoped SpanGuard + scoped ScopedSpanGuard
Separate the two responsibilities the old SpanGuard fused: span ownership
(thread-free) and scope activation (thread-bound TLS push).

- SpanGuard now owns ONLY the span. Its Impl drops the optional<Scope>,
  the owner thread-id, the Detached tag and the scope-less ctor; ~Impl is
  just `if (span) span->End()`. The guard never binds a thread-local
  context stack, so it may be moved to and destroyed on any thread.
- ScopedSpanGuard is a new pimpl type that wraps a SpanGuard plus an
  optional<Scope>. Member order (guard first, scope second) pops the
  scope before the span ends. It is non-copyable and non-movable;
  factories return unnamed temporaries so guaranteed copy elision covers
  `auto s = ScopedSpanGuard::freshRoot(...)`.
- `operator SpanGuard() &&` replaces detached()/detachInPlace(): it pops
  the scope eagerly on the origin thread and yields a thread-free
  SpanGuard for handoff to a job or another thread. detached(),
  detachInPlace() (both overloads) and the Detached apparatus are deleted.
- Move-assignment is re-enabled on SpanGuard (no Scope to re-bind), in
  both the real class and the no-op stub.
- rootSpan() renamed to freshRoot() in both classes (behavior unchanged:
  still forces kIsRootSpanKey).
- hashSpan behavior is intentionally unchanged (only builds the now
  unscoped Impl); the true-root IdGenerator fix is a separate later task.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 18:23:14 +01:00
Pratik Mankawde
dcfaf39b0a Merge branch 'pratik/otel-phase1b-telemetry-infra' into pratik/otel-phase1c-rpc-integration 2026-07-21 11:44:03 +01:00
Pratik Mankawde
f9b2d725a0 fix(telemetry): assert SpanGuard Scope is destroyed on its constructing thread
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 11:26:29 +01:00
Pratik Mankawde
1ed1cb9573 Merge branch 'pratik/otel-phase1b-telemetry-infra' into pratik/otel-phase1c-rpc-integration 2026-07-20 21:32:54 +01:00
Pratik Mankawde
b9c049d7c6 refactor(telemetry): add detachInPlace() helpers to remove hand-rolled SpanGuard re-detach idiom
Add two free functions in xrpl::telemetry that wrap the required
"detach a live SpanGuard held in a container" idiom so call sites write
one line instead of the make_shared/emplace rebuild by hand:

  void                detachInPlace(std::optional<SpanGuard>&);
  std::shared_ptr<SpanGuard> detachInPlace(std::shared_ptr<SpanGuard>);

Both are no-ops on an empty/null/inactive guard. The #else branch adds
matching inline no-op stubs so callers compile with telemetry disabled.
Pure API addition, no behavior change; consumer call sites are rewritten
on phase4 in a follow-up.

The unit tests for these helpers land on phase2, where the telemetry
test module (in-memory-exporter harness + SpanGuardScope.cpp) exists —
mirroring how the detached()/rootSpan() tests were placed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 21:30:05 +01:00
Pratik Mankawde
daea7fa34f Merge branch 'pratik/otel-phase1b-telemetry-infra' into pratik/otel-phase1c-rpc-integration
# Conflicts:
#	include/xrpl/telemetry/Telemetry.h
2026-07-20 17:17:20 +01:00
Pratik Mankawde
8b9cb43824 chore(telemetry): normalize Doxygen comment style + refresh levelization
Bring the 5 telemetry files introduced on this branch to the enforced
house Doxygen style (/** alone, ' * ' continuation prefix, no single-line
blocks) so the check-doxygen-style hook passes under CI's --all-files run.
Comment-only: code is byte-identical after comment stripping (verified).

Also regenerate levelization results: the committed ordering.txt carried
stale 'xrpl.telemetry > xrpld.consensus/rpc' edges that the current include
graph no longer produces.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 17:09:43 +01:00
Pratik Mankawde
98a2ad19fd Merge branch 'pratik/otel-phase1b-telemetry-infra' into pratik/otel-phase1c-rpc-integration
# Conflicts:
#	include/xrpl/telemetry/SpanGuard.h
2026-07-17 19:45:15 +01:00
Pratik Mankawde
4f9650502b feat(telemetry): add SpanGuard::detached() and rootSpan() to fix cross-thread scope leak
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>
2026-07-17 19:42:44 +01:00
Pratik Mankawde
868ee3eadd Merge branch 'pratik/otel-phase1b-telemetry-infra' into pratik/otel-phase1c-rpc-integration 2026-07-11 18:00:04 +01:00
Pratik Mankawde
a512009170 fix(telemetry): replace std::format with string concat for gcc-12
libstdc++ ships <format> starting with gcc-13, so std::format in
SpanGuard::span() fails to compile on gcc-12. Build the "<prefix>.<name>"
span name via std::string::append instead and drop the <format> include.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 17:59:33 +01:00
Pratik Mankawde
ba8785dc16 Merge branch 'pratik/otel-phase1b-telemetry-infra' into pratik/otel-phase1c-rpc-integration 2026-07-08 15:18:06 +01:00
Pratik Mankawde
c6be7cf83a fix(telemetry): resolve clang-tidy CI errors (include-cleaner, braces)
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>
2026-07-08 15:17:58 +01:00
Pratik Mankawde
60a2ac7bfe fix(telemetry): bound RPC error-path span cardinality; balance LCOV markers
Resolve the doCommand error-path span suffix and command attribute against
the handler registry instead of using raw request input. A recognized command
yields its canonical handler name; anything else (unregistered command, or
neither command/method present) collapses to the literal "unknown". This bounds
telemetry cardinality to the finite set of handler names plus "unknown" while
preserving per-command error attribution for known-but-rejected commands
(e.g. a submit that hit rpcTOO_BUSY stays rpc.command.submit). The command
attribute is promoted to a Prometheus label by the spanmetrics connector, so
attacker-controlled input previously enabled unbounded label cardinality.

Also add the missing LCOV_EXCL_STOP in the first SpanGuard::linkedSpan overload
so its coverage exclusion no longer bleeds into the second overload's guards.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 19:17:34 +01:00
Pratik Mankawde
b8a5361e28 Merge branch 'pratik/otel-phase1b-telemetry-infra' into pratik/otel-phase1c-rpc-integration 2026-06-26 17:37:54 +01:00
Pratik Mankawde
498bc3ee96 fix(telemetry): satisfy clang-tidy const-correctness and redundant-parens
CI enabled more clang-tidy checks via the upstream merge:
- DiscardScope discardScope -> const (misc-const-correctness)
- drop the (::max)() Windows macro-guard parens; NOMINMAX is defined
  project-wide so the bare form is correct (readability-redundant-parentheses)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 17:37:37 +01:00
Pratik Mankawde
58d217599b Merge branch 'pratik/otel-phase1b-telemetry-infra' into pratik/otel-phase1c-rpc-integration 2026-06-15 20:48:04 +01:00
Pratik Mankawde
039d6ccf4f fix(telemetry): use underscore form for span-link attribute key
The follows-from link attribute used the dotted xrpl.link.type form, which
is reserved for resource attributes. Span attributes use the <domain>_<field>
underscore form, so the key becomes link_type.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 20:47:57 +01:00
Pratik Mankawde
f9092e7948 Merge branch 'pratik/otel-phase1b-telemetry-infra' into pratik/otel-phase1c-rpc-integration
Signed-off-by: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com>
2026-06-15 20:39:08 +01:00
Pratik Mankawde
f0b834a913 minor naming change
Signed-off-by: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com>
2026-06-15 20:02:48 +01:00
Pratik Mankawde
ba85aecf00 hide gTlDiscardCurrentSpan better
Signed-off-by: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com>
2026-06-15 17:21:35 +01:00
Pratik Mankawde
37a4c9deb8 core review comments
Signed-off-by: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com>
2026-06-15 17:10:23 +01:00
Pratik Mankawde
8908036b11 Merge branch 'pratik/otel-phase1b-telemetry-infra' into pratik/otel-phase1c-rpc-integration 2026-06-10 14:55:29 +01:00
Pratik Mankawde
e205d0ef8e handle gTlDiscardCurrentSpan change
Signed-off-by: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com>
2026-06-10 14:00:52 +01:00
Pratik Mankawde
c128625857 Merge branch 'pratik/otel-phase1b-telemetry-infra' into pratik/otel-phase1c-rpc-integration 2026-06-10 10:43:54 +01:00
Pratik Mankawde
e11bf35691 fix: Update BasicConfig.h include path after upstream reorg
BasicConfig.h moved from xrpl/basics/ to xrpl/config/ on develop
(PR #7095 / reorg). Phase 1b's telemetry headers still referenced the
old path, breaking a fresh compile with "BasicConfig.h: No such file or
directory". Point both telemetry includes at the new location.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 10:42:52 +01:00
Pratik Mankawde
142e8c5b36 Merge branch 'pratik/otel-phase1b-telemetry-infra' into pratik/otel-phase1c-rpc-integration
Signed-off-by: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com>
2026-06-09 18:58:11 +01:00
Pratik Mankawde
c712968890 removed non-accessible include
Signed-off-by: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com>
2026-06-09 18:49:17 +01:00
Pratik Mankawde
fa71280795 removed head sampling ratio from config
Signed-off-by: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com>
2026-06-09 18:45:52 +01:00
Pratik Mankawde
a119efc478 Merge branch 'pratik/otel-phase1b-telemetry-infra' into pratik/otel-phase1c-rpc-integration 2026-06-09 13:44:53 +01:00
Pratik Mankawde
a946ce7458 feat(telemetry): enable peer tracing by default
Flip the tracePeer Setup default and the trace_peer config parser
default from off to on, and update the example config and build doc to
match. Peer spans record only peer_id (a node-local numeric connection
id) plus trust/ledger metadata — no IP addresses or public keys — so
the privacy concern behind disabling it does not apply. The high-volume
characteristic is retained in the docs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 13:43:32 +01:00
Pratik Mankawde
bdd7dea4b4 code coverage fixes
Signed-off-by: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com>
2026-06-03 14:23:24 +01:00
Pratik Mankawde
ded9847eaf Merge branch 'pratik/otel-phase1b-telemetry-infra' into pratik/otel-phase1c-rpc-integration 2026-06-01 13:42:01 +01:00
Pratik Mankawde
9918803333 clang tidy fixes
Signed-off-by: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com>
2026-06-01 13:18:27 +01:00
Pratik Mankawde
5d1e3f207c clang-tidy fixes
Signed-off-by: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com>
2026-06-01 11:35:09 +01:00
Pratik Mankawde
5dd5e765ae Merge branch 'pratik/otel-phase1b-telemetry-infra' into pratik/otel-phase1c-rpc-integration 2026-05-29 18:52:11 +01:00
Pratik Mankawde
c157253372 activate telemetry by default and fix clang-tidy issues.
Signed-off-by: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com>
2026-05-29 18:51:47 +01:00