From 3ad525a48a77df3241eb3e38d13601a4f66d68a9 Mon Sep 17 00:00:00 2001 From: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com> Date: Wed, 29 Jul 2026 14:33:05 +0100 Subject: [PATCH 1/2] =?UTF-8?q?docs(telemetry):=20drop=20speculative=20fil?= =?UTF-8?q?e/line=20estimate=20tables=20from=20=C2=A73.9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The §3.9.1 "Files Modified Summary" and §3.9.2 "Detailed File Impact" tables carried hand-maintained per-component line counts that had drifted from the plan: the Lines Added column summed to 1,565 while the Total row claimed ~1,670. Files (34) and Lines Changed (120) reconciled, so only the Added total was stale — residue from expanding Core Telemetry 5 -> 11 files. Rather than patch one cell, remove both tables. They were pre-implementation estimates with no source of truth, so any figure in them drifts again on the next phase. §3.1's directory tree remains the canonical list of the 11 new telemetry files, and §3.9.3-3.9.7 (risk, architectural impact, backward compatibility, rollback) carry the assessment without inventing line counts. No references to the removed sections exist elsewhere in the plan docs. --- .../03-implementation-strategy.md | 73 ------------------- 1 file changed, 73 deletions(-) diff --git a/OpenTelemetryPlan/03-implementation-strategy.md b/OpenTelemetryPlan/03-implementation-strategy.md index 4edcef6194..7cf0f9d445 100644 --- a/OpenTelemetryPlan/03-implementation-strategy.md +++ b/OpenTelemetryPlan/03-implementation-strategy.md @@ -339,79 +339,6 @@ Instrumentation is gated on two levels. A compile-time feature flag (`XRPL_ENABL This section provides a detailed assessment of how intrusive the OpenTelemetry integration is to the existing xrpld codebase. -### 3.9.1 Files Modified Summary - -| Component | Files Modified | Lines Added | Lines Changed | Architectural Impact | -| --------------------- | -------------- | ----------- | ------------- | -------------------- | -| **Core Telemetry** | 11 new files | ~980 | 0 | None (new module) | -| **Application Init** | 2 files | ~30 | ~5 | Minimal | -| **RPC Layer** | 3 files | ~80 | ~20 | Minimal | -| **Transaction Relay** | 4 files | ~120 | ~40 | Low | -| **Consensus** | 3 files | ~100 | ~30 | Low-Medium | -| **Protocol Buffers** | 1 file | ~25 | 0 | Low | -| **CMake/Build** | 3 files | ~50 | ~10 | Minimal | -| **PathFinding** | 2 | ~80 | ~5 | Minimal | -| **TxQ/Fee** | 2 | ~60 | ~5 | Minimal | -| **Validator/Amend** | 3 | ~40 | ~5 | Minimal | -| **Total** | **~34 files** | **~1,670** | **~120** | **Low** | - -### 3.9.2 Detailed File Impact - -```mermaid -pie title Code Changes by Component - "New Telemetry Module" : 800 - "Transaction Relay" : 160 - "Consensus" : 130 - "RPC Layer" : 100 - "PathFinding" : 80 - "TxQ/Fee" : 60 - "Validator/Amendment" : 40 - "Application Init" : 35 - "Protocol Buffers" : 25 - "Build System" : 60 -``` - -#### New Files (No Impact on Existing Code) - -| File | Lines | Purpose | -| ------------------------------------------------ | ----- | ------------------------ | -| `include/xrpl/telemetry/Telemetry.h` | ~160 | Main interface | -| `include/xrpl/telemetry/TelemetryConfig.h` | ~80 | Configuration structures | -| `include/xrpl/telemetry/TraceContext.h` | ~80 | Context propagation | -| `include/xrpl/telemetry/SpanGuard.h` | ~120 | RAII wrapper | -| `include/xrpl/telemetry/SpanAttributes.h` | ~60 | Attribute helpers | -| `src/libxrpl/telemetry/Telemetry.cpp` | ~200 | Implementation | -| `src/libxrpl/telemetry/TelemetryConfig.cpp` | ~60 | Config parsing | -| `src/libxrpl/telemetry/TraceContext.cpp` | ~80 | Context serialization | -| `src/libxrpl/telemetry/NullTelemetry.cpp` | ~40 | No-op implementation | -| `src/xrpld/telemetry/TracingInstrumentation.h` | ~60 | Macros | -| `src/xrpld/telemetry/TracingInstrumentation.cpp` | ~40 | Instrumentation impl | - -#### Modified Files (Existing Xrpld Code) - -| File | Lines Added | Lines Changed | Risk Level | -| ------------------------------------------------- | ----------- | ------------- | ---------- | -| `src/xrpld/app/main/Application.cpp` | ~15 | ~3 | Low | -| `include/xrpl/core/ServiceRegistry.h` | ~5 | ~2 | Low | -| `src/xrpld/rpc/detail/ServerHandler.cpp` | ~40 | ~10 | Low | -| `src/xrpld/rpc/handlers/*.cpp` | ~30 | ~8 | Low | -| `src/xrpld/overlay/detail/PeerImp.cpp` | ~60 | ~15 | Medium | -| `src/xrpld/overlay/detail/OverlayImpl.cpp` | ~30 | ~10 | Medium | -| `src/xrpld/app/consensus/RCLConsensus.cpp` | ~50 | ~15 | Medium | -| `src/xrpld/app/consensus/RCLConsensusAdaptor.cpp` | ~40 | ~12 | Medium | -| `src/xrpld/core/JobQueue.cpp` | ~20 | ~5 | Low | -| `src/xrpld/app/paths/PathRequest.cpp` | ~40 | ~3 | Low | -| `src/xrpld/app/paths/Pathfinder.cpp` | ~40 | ~2 | Low | -| `src/xrpld/app/misc/TxQ.cpp` | ~40 | ~3 | Low | -| `src/xrpld/app/main/LoadManager.cpp` | ~20 | ~2 | Low | -| `src/xrpld/app/misc/ValidatorList.cpp` | ~20 | ~2 | Low | -| `src/xrpld/app/misc/AmendmentTable.cpp` | ~10 | ~2 | Low | -| `src/xrpld/app/misc/Manifest.cpp` | ~10 | ~1 | Low | -| `src/xrpld/shamap/SHAMap.cpp` | ~20 | ~3 | Low | -| `src/xrpld/overlay/detail/ripple.proto` | ~25 | 0 | Low | -| `CMakeLists.txt` | ~40 | ~8 | Low | -| `cmake/FindOpenTelemetry.cmake` | ~50 | 0 | None (new) | - ### 3.9.3 Risk Assessment by Component
From 17ff797633db4b7eb006dedac8c566dfe93f3af8 Mon Sep 17 00:00:00 2001 From: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com> Date: Wed, 29 Jul 2026 15:06:31 +0100 Subject: [PATCH 2/2] refactor(telemetry): build span names with std::format SpanGuard::span() and freshRoot() joined the prefix and suffix by hand with reserve() + three append() calls. std::format expresses the same join in one line; libstdc++ has shipped since GCC 13 and the project floor is GCC 15.2 (BUILD.md), so it is available on every supported compiler. Both callers are noexcept and std::format can throw (std::bad_alloc, or std::format_error on a malformed spec), so an escaping exception would terminate the process. The call is wrapped in a joinSpanName() helper that catches and returns std::nullopt; the caller then returns a null guard, which is the same degrade-to-no-op path already taken when telemetry is disabled. Telemetry must never bring the node down. One helper rather than a try/catch at each site keeps both call sites a single line and puts the exception-safety requirement in one documented place. Addresses a review comment on PR #6437. --- src/libxrpl/telemetry/SpanGuard.cpp | 45 ++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/src/libxrpl/telemetry/SpanGuard.cpp b/src/libxrpl/telemetry/SpanGuard.cpp index 84b23f44cd..a165d0ad5a 100644 --- a/src/libxrpl/telemetry/SpanGuard.cpp +++ b/src/libxrpl/telemetry/SpanGuard.cpp @@ -47,6 +47,7 @@ #include #include +#include #include #include #include @@ -186,6 +187,34 @@ categoryToSpanKind(TraceCategory cat) return otel_trace::SpanKind::kInternal; // unreachable } +/** + * Join a span-name prefix and suffix into the dotted full name. + * + * Wraps std::format because the callers are noexcept: std::format can throw + * (std::bad_alloc, or std::format_error on a malformed spec) and an escaping + * exception would terminate the process. Telemetry must never take the node + * down, so a failure yields std::nullopt and the caller returns a null guard — + * the same degrade-to-no-op path already used when telemetry is disabled. + * + * @param prefix Segment before the dot (e.g. "consensus"). + * @param name Segment after the dot (e.g. "round"). + * @return The joined name, or std::nullopt if formatting failed. + */ +[[nodiscard]] std::optional +joinSpanName(std::string_view prefix, std::string_view name) noexcept +{ + try + { + return std::format("{}.{}", prefix, name); + } + catch (std::exception const&) + { + // Out of memory or a bad format spec. Drop the span rather than + // propagate out of a noexcept factory. + return std::nullopt; + } +} + } // namespace SpanGuard @@ -194,10 +223,10 @@ SpanGuard::span(TraceCategory cat, std::string_view prefix, std::string_view nam auto* tel = Telemetry::getInstance(); if ((tel == nullptr) || !tel->isEnabled() || !isCategoryEnabled(*tel, cat)) return {}; - std::string fullName; - fullName.reserve(prefix.size() + 1 + name.size()); - fullName.append(prefix).append(1, '.').append(name); - return SpanGuard(std::make_unique(tel->startSpan(fullName, categoryToSpanKind(cat)))); + auto const fullName = joinSpanName(prefix, name); + if (!fullName) + return {}; + return SpanGuard(std::make_unique(tel->startSpan(*fullName, categoryToSpanKind(cat)))); } SpanGuard @@ -206,13 +235,13 @@ SpanGuard::freshRoot(TraceCategory cat, std::string_view prefix, std::string_vie auto* tel = Telemetry::getInstance(); if ((tel == nullptr) || !tel->isEnabled() || !isCategoryEnabled(*tel, cat)) return {}; - std::string fullName; - fullName.reserve(prefix.size() + 1 + name.size()); - fullName.append(prefix).append(1, '.').append(name); + auto const fullName = joinSpanName(prefix, name); + if (!fullName) + return {}; // Force a fresh trace root: do NOT inherit this thread's active span. auto rootCtx = opentelemetry::context::Context{otel_trace::kIsRootSpanKey, true}; return SpanGuard( - std::make_unique(tel->startSpan(fullName, rootCtx, categoryToSpanKind(cat)))); + std::make_unique(tel->startSpan(*fullName, rootCtx, categoryToSpanKind(cat)))); } // ===== Child / linked span creation ========================================