Merge branch 'pratik/otel-phase1c-rpc-integration' into pratik/otel-phase2-rpc-tracing

# Conflicts:
#	.github/scripts/levelization/results/loops.txt
This commit is contained in:
Pratik Mankawde
2026-07-20 17:18:53 +01:00
14 changed files with 624 additions and 472 deletions

View File

@@ -1,14 +1,15 @@
/** No-op implementation of the Telemetry interface.
Always compiled (regardless of XRPL_ENABLE_TELEMETRY). Provides the
makeTelemetry() factory when telemetry is compiled out (#ifndef), which
unconditionally returns a NullTelemetry that does nothing.
When XRPL_ENABLE_TELEMETRY IS defined, the OTel virtual methods
(getTracer, startSpan) return noop tracers/spans. The makeTelemetry()
factory in this file is not used in that case -- Telemetry.cpp provides
its own factory that can return the real TelemetryImpl.
*/
/**
* No-op implementation of the Telemetry interface.
*
* Always compiled (regardless of XRPL_ENABLE_TELEMETRY). Provides the
* makeTelemetry() factory when telemetry is compiled out (#ifndef), which
* unconditionally returns a NullTelemetry that does nothing.
*
* When XRPL_ENABLE_TELEMETRY IS defined, the OTel virtual methods
* (getTracer, startSpan) return noop tracers/spans. The makeTelemetry()
* factory in this file is not used in that case -- Telemetry.cpp provides
* its own factory that can return the real TelemetryImpl.
*/
#include <xrpl/telemetry/Telemetry.h>
@@ -30,14 +31,17 @@ namespace xrpl::telemetry {
namespace {
/** No-op Telemetry that returns immediately from every method.
Used as the sole implementation when XRPL_ENABLE_TELEMETRY is not
defined, or as a fallback when it is defined but enabled=0.
*/
/**
* No-op Telemetry that returns immediately from every method.
*
* Used as the sole implementation when XRPL_ENABLE_TELEMETRY is not
* defined, or as a fallback when it is defined but enabled=0.
*/
class NullTelemetry : public Telemetry
{
/** Retained configuration (unused, kept for diagnostic access). */
/**
* Retained configuration (unused, kept for diagnostic access).
*/
Setup const setup_;
public:
@@ -123,9 +127,10 @@ public:
} // namespace
/** Factory used when XRPL_ENABLE_TELEMETRY is not defined.
Unconditionally returns a NullTelemetry instance.
*/
/**
* Factory used when XRPL_ENABLE_TELEMETRY is not defined.
* Unconditionally returns a NullTelemetry instance.
*/
#ifndef XRPL_ENABLE_TELEMETRY
std::unique_ptr<Telemetry>
makeTelemetry(Telemetry::Setup const& setup, beast::Journal)

View File

@@ -1,17 +1,18 @@
/** OpenTelemetry SDK implementation of the Telemetry interface.
Compiled only when XRPL_ENABLE_TELEMETRY is defined (via CMake
telemetry=ON). Contains:
- FilteringSpanProcessor: decorator that drops spans marked with
kDiscardedAttr before they enter the batch export queue.
- TelemetryImpl: configures the OTel SDK with an OTLP/HTTP exporter,
FilteringSpanProcessor wrapping a batch span processor,
trace-ID-ratio sampler, and resource attributes.
- NullTelemetryOtel: no-op fallback used when telemetry is compiled in
but disabled at runtime (enabled=0 in config).
- makeTelemetry(): factory that selects the appropriate implementation.
*/
/**
* OpenTelemetry SDK implementation of the Telemetry interface.
*
* Compiled only when XRPL_ENABLE_TELEMETRY is defined (via CMake
* telemetry=ON). Contains:
*
* - FilteringSpanProcessor: decorator that drops spans marked with
* kDiscardedAttr before they enter the batch export queue.
* - TelemetryImpl: configures the OTel SDK with an OTLP/HTTP exporter,
* FilteringSpanProcessor wrapping a batch span processor,
* trace-ID-ratio sampler, and resource attributes.
* - NullTelemetryOtel: no-op fallback used when telemetry is compiled in
* but disabled at runtime (enabled=0 in config).
* - makeTelemetry(): factory that selects the appropriate implementation.
*/
#ifdef XRPL_ENABLE_TELEMETRY
@@ -60,39 +61,40 @@ namespace trace_sdk = opentelemetry::sdk::trace;
namespace otlp_http = opentelemetry::exporter::otlp;
namespace resource = opentelemetry::sdk::resource;
/** SpanProcessor decorator that drops discarded spans.
Wraps a delegate processor (typically BatchSpanProcessor). In OnEnd(),
calls DiscardScope::isActive(). If the calling thread is inside a
DiscardScope (entered by SpanGuard::discard()), the span is silently
dropped — never entering the batch queue, never sent over the network,
never stored.
Uses a thread-local flag rather than inspecting Recordable attributes
because the Recordable type varies by exporter (SpanData for simple
exporters, OtlpRecordable for OTLP) and none expose a uniform getter.
The flag is safe because Span::End() calls OnEnd() synchronously on
the same thread.
All other methods delegate directly to the wrapped processor.
Dependency diagram:
+---------------------------+
| FilteringSpanProcessor |
+---------------------------+
| - delegate_ : unique_ptr |
| <SpanProcessor> |
+---------------------------+
| wraps
+---------+-----------+
| BatchSpanProcessor |
+---------------------+
@note Thread safety: OnEnd() may be called concurrently from multiple
threads. The discard flag behind DiscardScope is thread-local, so each
thread's discard state is independent — no synchronization needed.
*/
/**
* SpanProcessor decorator that drops discarded spans.
*
* Wraps a delegate processor (typically BatchSpanProcessor). In OnEnd(),
* calls DiscardScope::isActive(). If the calling thread is inside a
* DiscardScope (entered by SpanGuard::discard()), the span is silently
* dropped — never entering the batch queue, never sent over the network,
* never stored.
*
* Uses a thread-local flag rather than inspecting Recordable attributes
* because the Recordable type varies by exporter (SpanData for simple
* exporters, OtlpRecordable for OTLP) and none expose a uniform getter.
* The flag is safe because Span::End() calls OnEnd() synchronously on
* the same thread.
*
* All other methods delegate directly to the wrapped processor.
*
* Dependency diagram:
*
* +---------------------------+
* | FilteringSpanProcessor |
* +---------------------------+
* | - delegate_ : unique_ptr |
* | <SpanProcessor> |
* +---------------------------+
* | wraps
* +---------+-----------+
* | BatchSpanProcessor |
* +---------------------+
*
* @note Thread safety: OnEnd() may be called concurrently from multiple
* threads. The discard flag behind DiscardScope is thread-local, so each
* thread's discard state is independent — no synchronization needed.
*/
class FilteringSpanProcessor : public trace_sdk::SpanProcessor
{
std::unique_ptr<trace_sdk::SpanProcessor> delegate_;
@@ -144,15 +146,18 @@ public:
}
};
/** No-op implementation used when XRPL_ENABLE_TELEMETRY is defined but
setup.enabled is false at runtime.
Lives in the anonymous namespace so there is no ODR conflict with the
NullTelemetry in NullTelemetry.cpp.
*/
/**
* No-op implementation used when XRPL_ENABLE_TELEMETRY is defined but
* setup.enabled is false at runtime.
*
* Lives in the anonymous namespace so there is no ODR conflict with the
* NullTelemetry in NullTelemetry.cpp.
*/
class NullTelemetryOtel : public Telemetry
{
/** Retained configuration (unused, kept for diagnostic access). */
/**
* Retained configuration (unused, kept for diagnostic access).
*/
Setup const setup_;
public:
@@ -230,27 +235,32 @@ public:
}
};
/** Full OTel SDK implementation that exports trace spans via OTLP/HTTP.
Configures an OTLP/HTTP exporter, batch span processor,
TraceIdRatioBasedSampler, and resource attributes on start().
*/
/**
* Full OTel SDK implementation that exports trace spans via OTLP/HTTP.
*
* Configures an OTLP/HTTP exporter, batch span processor,
* TraceIdRatioBasedSampler, and resource attributes on start().
*/
class TelemetryImpl : public Telemetry
{
/** Configuration from the [telemetry] config section.
Non-const so setServiceInstanceId() can update the instance ID
before start() creates the OTel resource.
*/
/**
* Configuration from the [telemetry] config section.
* Non-const so setServiceInstanceId() can update the instance ID
* before start() creates the OTel resource.
*/
Setup setup_;
/** Journal used for log output during start/stop. */
/**
* Journal used for log output during start/stop.
*/
beast::Journal const journal_;
/** The SDK TracerProvider that owns the export pipeline.
Held as std::shared_ptr so we can call ForceFlush() on shutdown.
Wrapped in a nostd::shared_ptr when registered as the global provider.
*/
/**
* The SDK TracerProvider that owns the export pipeline.
*
* Held as std::shared_ptr so we can call ForceFlush() on shutdown.
* Wrapped in a nostd::shared_ptr when registered as the global provider.
*/
std::shared_ptr<trace_sdk::TracerProvider> sdkProvider_;
public:

View File

@@ -1,11 +1,12 @@
/** Parser for the [telemetry] section of xrpld.cfg.
Reads configuration values from the config file and populates a
Telemetry::Setup struct. All options have sensible defaults so the
section can be minimal or omitted entirely.
See cfg/xrpld-example.cfg for the full list of available options.
*/
/**
* Parser for the [telemetry] section of xrpld.cfg.
*
* Reads configuration values from the config file and populates a
* Telemetry::Setup struct. All options have sensible defaults so the
* section can be minimal or omitted entirely.
*
* See cfg/xrpld-example.cfg for the full list of available options.
*/
#include <xrpl/config/BasicConfig.h>
#include <xrpl/telemetry/Telemetry.h>
@@ -18,13 +19,14 @@ namespace xrpl::telemetry {
namespace {
/** Config key names for the [telemetry] section.
Each must match the corresponding option documented in
cfg/xrpld-example.cfg verbatim. Defined as `char const*` so they
pass to Section::valueOr() (which takes `std::string const&`)
without an explicit conversion, exactly as a literal would.
*/
/**
* Config key names for the [telemetry] section.
*
* Each must match the corresponding option documented in
* cfg/xrpld-example.cfg verbatim. Defined as `char const*` so they
* pass to Section::valueOr() (which takes `std::string const&`)
* without an explicit conversion, exactly as a literal would.
*/
namespace key {
constexpr char const* enabled = "enabled";
constexpr char const* serviceName = "service_name";
@@ -42,13 +44,14 @@ constexpr char const* tracePeer = "trace_peer";
constexpr char const* traceLedger = "trace_ledger";
} // namespace key
/** Default values applied when a key is absent from the config.
@note serviceName mirrors SystemParameters' systemName() ("xrpld") but
is duplicated here as a literal: the telemetry module deliberately does
not link xrpl.libxrpl.protocol, so including SystemParameters.h would
introduce an undeclared cross-module dependency.
*/
/**
* Default values applied when a key is absent from the config.
*
* @note serviceName mirrors SystemParameters' systemName() ("xrpld") but
* is duplicated here as a literal: the telemetry module deliberately does
* not link xrpl.libxrpl.protocol, so including SystemParameters.h would
* introduce an undeclared cross-module dependency.
*/
namespace dflt {
constexpr char const* serviceName = "xrpld";
constexpr char const* endpoint = "http://localhost:4318/v1/traces";
@@ -57,10 +60,11 @@ constexpr std::uint32_t batchDelayMs = 5000u;
constexpr std::uint32_t maxQueueSize = 2048u;
} // namespace dflt
/** Derive a human-readable network type label from the numeric network ID.
@param networkId The network identifier from [network_id] config.
@return "mainnet", "testnet", "devnet", or "unknown" for other values.
*/
/**
* Derive a human-readable network type label from the numeric network ID.
* @param networkId The network identifier from [network_id] config.
* @return "mainnet", "testnet", "devnet", or "unknown" for other values.
*/
std::string
networkTypeFromId(std::uint32_t networkId)
{

View File

@@ -197,7 +197,9 @@ private:
std::vector<boost::asio::ip::address> const& secureGatewayIPs_;
/// Human-readable name for telemetry spans (e.g. "GetLedger").
/**
* Human-readable name for telemetry spans (e.g. "GetLedger").
*/
std::string_view name_;
public:

View File

@@ -1,6 +1,7 @@
#pragma once
/** Compile-time span name constants for the gRPC subsystem.
/**
* Compile-time span name constants for the gRPC subsystem.
*
* All span prefixes, operation names, and attribute keys used by gRPC
* tracing call sites are defined here. Built on the StaticStr/join()
@@ -28,19 +29,27 @@ namespace xrpl::telemetry::grpc_span {
// ===== Span prefixes =======================================================
namespace prefix {
/// "grpc" — root prefix for gRPC transport spans. The full span name is
/// formed at the call site as `grpc.<MethodName>` (see GRPCServer.cpp).
/**
* "grpc" — root prefix for gRPC transport spans. The full span name is
* formed at the call site as `grpc.<MethodName>` (see GRPCServer.cpp).
*/
inline constexpr auto grpc = makeStr("grpc");
} // namespace prefix
// ===== Attribute keys ======================================================
namespace attr {
/// "method" — gRPC method name (e.g. GetLedger).
/**
* "method" — gRPC method name (e.g. GetLedger).
*/
inline constexpr auto method = makeStr("method");
/// "grpc_role" — Domain-qualified: collides with rpc_role.
/**
* "grpc_role" — Domain-qualified: collides with rpc_role.
*/
inline constexpr auto grpcRole = makeStr("grpc_role");
/// "grpc_status" — Domain-qualified: avoids OTel reserved span status.
/**
* "grpc_status" — Domain-qualified: avoids OTel reserved span status.
*/
inline constexpr auto grpcStatus = makeStr("grpc_status");
} // namespace attr

View File

@@ -192,10 +192,11 @@ private:
void
processSession(std::shared_ptr<Session> const&, std::shared_ptr<JobQueue::Coro> coro);
/** Process an RPC request and write the reply to `output`.
@return false if the request resulted in an error response, true
otherwise. Lets the caller's enclosing span reflect the outcome.
*/
/**
* Process an RPC request and write the reply to `output`.
* @return false if the request resulted in an error response, true
* otherwise. Lets the caller's enclosing span reflect the outcome.
*/
bool
processRequest(
Port const& port,

View File

@@ -1,6 +1,7 @@
#pragma once
/** Compile-time span name constants for PathFind tracing.
/**
* Compile-time span name constants for PathFind tracing.
*
* Covers the path_find and ripple_path_find RPC handlers, the
* PathRequest computation engine, and the Pathfinder graph exploration.
@@ -48,7 +49,9 @@ namespace xrpl::telemetry::pathfind_span {
// ===== Span prefixes =======================================================
namespace prefix {
/// "pathfind" — root prefix for path finding spans.
/**
* "pathfind" — root prefix for path finding spans.
*/
inline constexpr auto pathfind = makeStr("pathfind");
} // namespace prefix
@@ -68,25 +71,43 @@ inline constexpr auto discover = makeStr("discover");
// `fast` or `num_paths` that other subsystems may introduce.
namespace attr {
/// "pathfind_source_account" — originating account for path search.
/**
* "pathfind_source_account" — originating account for path search.
*/
inline constexpr auto sourceAccount = makeStr("pathfind_source_account");
/// "pathfind_dest_account" — destination account.
/**
* "pathfind_dest_account" — destination account.
*/
inline constexpr auto destAccount = makeStr("pathfind_dest_account");
/// "pathfind_fast" — whether fast pathfinding mode enabled.
/**
* "pathfind_fast" — whether fast pathfinding mode enabled.
*/
inline constexpr auto fast = makeStr("pathfind_fast");
/// "pathfind_search_level" — depth of graph exploration.
/**
* "pathfind_search_level" — depth of graph exploration.
*/
inline constexpr auto searchLevel = makeStr("pathfind_search_level");
/// "pathfind_num_paths" — total paths produced across the per-source-asset
/// loop in PathRequest::findPaths (sum of getBestPaths().size() per asset).
/**
* "pathfind_num_paths" — total paths produced across the per-source-asset
* loop in PathRequest::findPaths (sum of getBestPaths().size() per asset).
*/
inline constexpr auto numPaths = makeStr("pathfind_num_paths");
/// "pathfind_num_requests" — snapshot size of requests_ at update_all start
/// (may include weak_ptrs that subsequently expire during processing).
/**
* "pathfind_num_requests" — snapshot size of requests_ at update_all start
* (may include weak_ptrs that subsequently expire during processing).
*/
inline constexpr auto numRequests = makeStr("pathfind_num_requests");
/// "pathfind_ledger_index" — pathfind target ledger index.
/**
* "pathfind_ledger_index" — pathfind target ledger index.
*/
inline constexpr auto ledgerIndex = makeStr("pathfind_ledger_index");
/// "pathfind_dest_currency" — destination currency code.
/**
* "pathfind_dest_currency" — destination currency code.
*/
inline constexpr auto destCurrency = makeStr("pathfind_dest_currency");
/// "pathfind_num_source_assets" — candidate source assets count.
/**
* "pathfind_num_source_assets" — candidate source assets count.
*/
inline constexpr auto numSourceAssets = makeStr("pathfind_num_source_assets");
} // namespace attr

View File

@@ -1,6 +1,7 @@
#pragma once
/** Compile-time span name constants for the RPC subsystem.
/**
* Compile-time span name constants for the RPC subsystem.
*
* All span prefixes, operation names, and attribute keys used by RPC
* tracing call sites are defined here. Built on the StaticStr/join()
@@ -116,9 +117,13 @@ namespace xrpl::telemetry::rpc_span {
// ===== Span prefixes =======================================================
namespace prefix {
/// "rpc" — root prefix for transport-level spans.
/**
* "rpc" — root prefix for transport-level spans.
*/
inline constexpr auto rpc = seg::rpc;
/// "rpc.command" — prefix for individual RPC command spans.
/**
* "rpc.command" — prefix for individual RPC command spans.
*/
inline constexpr auto command = join(seg::rpc, makeStr("command"));
} // namespace prefix
@@ -134,21 +139,37 @@ inline constexpr auto process = makeStr("process");
// ===== Attribute keys ======================================================
namespace attr {
/// "command" — RPC method name.
/**
* "command" — RPC method name.
*/
inline constexpr auto command = makeStr("command");
/// "version" — api_version per request.
/**
* "version" — api_version per request.
*/
inline constexpr auto version = makeStr("version");
/// "rpc_role" — admin|user. Domain-qualified: collides with grpc_role.
/**
* "rpc_role" — admin|user. Domain-qualified: collides with grpc_role.
*/
inline constexpr auto rpcRole = makeStr("rpc_role");
/// "rpc_status" — success|error. Domain-qualified: avoids OTel reserved span status.
/**
* "rpc_status" — success|error. Domain-qualified: avoids OTel reserved span status.
*/
inline constexpr auto rpcStatus = makeStr("rpc_status");
/// "request_payload_size" — bytes of inbound request payload.
/**
* "request_payload_size" — bytes of inbound request payload.
*/
inline constexpr auto requestPayloadSize = makeStr("request_payload_size");
/// "is_batch" — whether request is a JSON-RPC batch.
/**
* "is_batch" — whether request is a JSON-RPC batch.
*/
inline constexpr auto isBatch = makeStr("is_batch");
/// "batch_size" — number of sub-requests in a batch.
/**
* "batch_size" — number of sub-requests in a batch.
*/
inline constexpr auto batchSize = makeStr("batch_size");
/// "load_type" — resource cost category after execution.
/**
* "load_type" — resource cost category after execution.
*/
inline constexpr auto loadType = makeStr("load_type");
} // namespace attr
@@ -160,7 +181,9 @@ using telemetry::attr_val::success;
inline constexpr auto admin = makeStr("admin");
inline constexpr auto user = makeStr("user");
inline constexpr auto unknownCommand = makeStr("unknown");
/// "invalid_json" — WS message parse failure or oversize.
/**
* "invalid_json" — WS message parse failure or oversize.
*/
inline constexpr auto invalidJson = makeStr("invalid_json");
} // namespace val