fix(telemetry): address review findings and PR #6437 comments

Critical fixes:
- Restore accidentally removed mallocTrim call and MallocTrim.h include
- Add missing shouldTraceLedger() to interface and all implementations
- Derive networkId/networkType from config_->NETWORK_ID (0=mainnet,
  1=testnet, 2=devnet) instead of leaving defaults unpopulated
- Clamp sampling_ratio to [0.0, 1.0] in config parser

PR comment fixes:
- Rename rippled -> xrpld in service name defaults, getTracer() calls,
  Docker network, comments, and docs/build/telemetry.md
- Remove exporter config option (only otlp_http supported)
- Add trace_ledger and service_name to example config
- Clarify head-based sampling semantics in config comments
- Add filter descriptions for span intrinsic filters in Grafana datasource
- Add inline comments to Docker Compose services

Docker/config improvements:
- Remove deprecated version: "3.8" from docker-compose.yml
- Pin images: collector 0.121.0, grafana 11.5.2
- Add health_check extension to otel-collector-config.yaml
- Comment out Tempo metrics_generator remote_write (no Prometheus service)
- Add Prometheus datasource caveat in Grafana datasource config

Other:
- Revert unrelated formatting changes in ServiceRegistry.h
- Change Conan telemetry default to False (matches CMake OFF)
- Add CLAUDE.md-required docs (ASCII diagrams, usage examples,
  @note thread-safety) to Telemetry.h and SpanGuard.h

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Pratik Mankawde
2026-04-16 17:07:01 +01:00
parent ea921d3a02
commit 3852b5ae4b
14 changed files with 263 additions and 69 deletions

View File

@@ -25,17 +25,18 @@ class Telemetry;
// This is temporary until we migrate all code to use ServiceRegistry.
class Application;
template <class Key,
class T,
bool IsKeyCache,
class SharedWeakUnionPointer,
class SharedPointerType,
class Hash,
class KeyEqual,
class Mutex>
template <
class Key,
class T,
bool IsKeyCache,
class SharedWeakUnionPointer,
class SharedPointerType,
class Hash,
class KeyEqual,
class Mutex>
class TaggedCache;
class STLedgerEntry;
using SLE = STLedgerEntry;
using SLE = STLedgerEntry;
using CachedSLEs = TaggedCache<uint256, SLE const>;
// Forward declarations
@@ -93,7 +94,7 @@ using NodeCache = TaggedCache<SHAMapHash, Blob>;
class ServiceRegistry
{
public:
ServiceRegistry() = default;
ServiceRegistry() = default;
virtual ~ServiceRegistry() = default;
// Core infrastructure services

View File

@@ -6,10 +6,65 @@
activated on the current thread's context (via Scope). On destruction,
the span is ended and the previous context is restored.
Dependency diagram:
+------------------------------------+
| SpanGuard |
+------------------------------------+
| - span_ : shared_ptr<Span> |
| - scope_ : Scope |
+------------------------------------+
| uses
+-------+-------+
| |
+--------+ +-------------+
| Span | | Scope |
| (OTel) | | (OTel, non- |
| | | movable) |
+--------+ +-------------+
Used by the XRPL_TRACE_* macros in TracingInstrumentation.h. Can also
be stored in std::optional for conditional tracing (move-constructible).
Only compiled when XRPL_ENABLE_TELEMETRY is defined.
Usage examples:
1. Basic RAII tracing:
@code
{
SpanGuard guard(telemetry.startSpan("rpc.command.submit"));
guard.setAttribute("xrpl.rpc.command", "submit");
// ... span is active on this thread's context
} // span ended, previous context restored
@endcode
2. Conditional tracing with std::optional:
@code
std::optional<SpanGuard> guard;
if (telemetry.isEnabled() && telemetry.shouldTraceRpc())
guard.emplace(telemetry.startSpan("rpc.request"));
// ... guard may or may not hold a span
@endcode
3. Error recording:
@code
SpanGuard guard(telemetry.startSpan("rpc.command.submit"));
try {
// ... do work
guard.setOk();
} catch (std::exception const& e) {
guard.recordException(e); // sets status to error
}
@endcode
@note Thread safety: A SpanGuard must only be used on the thread where
it was constructed (the Scope binds to the thread-local context stack).
Use context() to propagate the trace to other threads.
@note Limitation: Move assignment is deleted because re-scoping a span
mid-flight would corrupt the context stack. Only move construction is
supported (for std::optional emplacement).
*/
#ifdef XRPL_ENABLE_TELEMETRY

View File

@@ -3,19 +3,70 @@
/** Abstract interface for OpenTelemetry distributed tracing.
Provides the Telemetry base class that all components use to create trace
spans. Two implementations exist:
spans. Two concrete implementations exist, selected at construction time
by make_Telemetry():
- TelemetryImpl (Telemetry.cpp): real OTel SDK integration, compiled
only when XRPL_ENABLE_TELEMETRY is defined and enabled at runtime.
- NullTelemetry (NullTelemetry.cpp): no-op stub used when telemetry is
disabled at compile time or runtime.
Inheritance / dependency diagram:
+--------------------+
| Telemetry | (abstract, this file)
| <<interface>> |
+---------+----------+
|
+---------+-----------+-------------------+
| | |
+---+------------+ +-----+---------+ +------+----------+
| TelemetryImpl | | NullTelemetry | | NullTelemetryOtel|
| (Telemetry.cpp)| |(NullTelemetry | | (Telemetry.cpp) |
| OTel SDK | | .cpp) | | noop w/ OTel API |
+----------------+ +---------------+ +------------------+
The Setup struct holds all configuration parsed from the [telemetry]
section of xrpld.cfg. See TelemetryConfig.cpp for the parser and
cfg/xrpld-example.cfg for the available options.
OTel SDK headers are conditionally included behind XRPL_ENABLE_TELEMETRY
so that builds without telemetry have zero dependency on opentelemetry-cpp.
Usage examples:
1. Check before tracing (typical guard pattern):
@code
auto& telemetry = registry.getTelemetry();
if (telemetry.isEnabled() && telemetry.shouldTraceRpc())
{
auto span = telemetry.startSpan("rpc.command.server_info");
// ... do work, span ends when shared_ptr refcount drops to 0
}
@endcode
2. RAII tracing with SpanGuard (preferred):
@code
if (telemetry.isEnabled() && telemetry.shouldTraceRpc())
{
SpanGuard guard(telemetry.startSpan("rpc.command.submit"));
guard.setAttribute("xrpl.rpc.command", "submit");
// ... guard ends span automatically on scope exit
}
@endcode
3. Cross-thread context propagation:
@code
// On thread A: capture context
auto ctx = guard.context();
// On thread B: create child span with explicit parent
auto child = telemetry.startSpan("async.work", ctx);
@endcode
@note Thread safety: The Telemetry interface is safe for concurrent reads
(isEnabled, shouldTrace*, getTracer, startSpan) after start() completes.
setServiceInstanceId() must be called before start() and is not thread-safe.
The OTel SDK's TracerProvider and Tracer are internally thread-safe.
*/
#include <xrpl/basics/BasicConfig.h>
@@ -50,7 +101,7 @@ public:
bool enabled = false;
/** OTel resource attribute `service.name`. */
std::string serviceName = "rippled";
std::string serviceName = "xrpld";
/** OTel resource attribute `service.version` (set from BuildInfo). */
std::string serviceVersion;
@@ -59,9 +110,6 @@ public:
public key). */
std::string serviceInstanceId;
/** Exporter type: currently only "otlp_http" is supported. */
std::string exporterType = "otlp_http";
/** OTLP/HTTP endpoint URL where spans are sent. */
std::string exporterEndpoint = "http://localhost:4318/v1/traces";
@@ -157,6 +205,10 @@ public:
virtual bool
shouldTracePeer() const = 0;
/** @return true if ledger close/accept should be traced. */
virtual bool
shouldTraceLedger() const = 0;
#ifdef XRPL_ENABLE_TELEMETRY
/** Get or create a named tracer instance.
@@ -164,7 +216,7 @@ public:
@return A shared pointer to the Tracer.
*/
virtual opentelemetry::nostd::shared_ptr<opentelemetry::trace::Tracer>
getTracer(std::string_view name = "rippled") = 0;
getTracer(std::string_view name = "xrpld") = 0;
/** Start a new span on the current thread's context.
@@ -214,13 +266,16 @@ make_Telemetry(Telemetry::Setup const& setup, beast::Journal journal);
@param section The [telemetry] config section.
@param nodePublicKey Node public key, used as default instance ID.
@param version Build version string.
@param networkId Network identifier from [network_id] config
(0 = mainnet, 1 = testnet, 2 = devnet).
@return A populated Setup struct with defaults for missing values.
*/
Telemetry::Setup
setup_Telemetry(
Section const& section,
std::string const& nodePublicKey,
std::string const& version);
std::string const& version,
std::uint32_t networkId);
} // namespace telemetry
} // namespace xrpl