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,6 +1,3 @@
Loop: xrpl.telemetry xrpld.rpc
xrpld.rpc > xrpl.telemetry
Loop: xrpld.app xrpld.overlay
xrpld.app > xrpld.overlay

View File

@@ -315,6 +315,7 @@ xrpld.rpc > xrpl.rdb
xrpld.rpc > xrpl.resource
xrpld.rpc > xrpl.server
xrpld.rpc > xrpl.shamap
xrpld.rpc > xrpl.telemetry
xrpld.rpc > xrpl.tx
xrpld.shamap > xrpl.basics
xrpld.shamap > xrpld.core

View File

@@ -1,56 +1,58 @@
#pragma once
/** Thread-local discard signaling between SpanGuard and the span processor.
SpanGuard::discard() wants to drop a span without sending it to the
exporter. The OTel SDK calls SpanProcessor::OnEnd() synchronously on the
same thread that calls Span::End(), so a thread-local flag set just before
End() and read inside OnEnd() lets FilteringSpanProcessor drop the span
before it enters the batch export queue.
This side-channel avoids inspecting the Recordable's internals (which vary
by exporter type — SpanData vs OtlpRecordable).
The flag is a *private* thread-local member of DiscardScope, mutated only by
its constructor and destructor. This gives real access control rather than a
naming convention: no code that includes this header can flip the flag
directly — it can only enter a DiscardScope (which sets and clears the flag
over its own lifetime) and observe the state via DiscardScope::isActive().
Binding set/clear to a scope also means the flag cannot leak onto the next
span even if End() were to throw.
Kept in a separate header to avoid transitive include bloat: SpanGuard.h
only needs this signaling, not the full Telemetry.h with BasicConfig/Journal.
Usage:
@code
// In SpanGuard::discard():
{
DiscardScope discardScope; // flag set for this scope only
span->End(); // OnEnd() runs synchronously, sees flag
} // flag cleared here, unconditionally
// In FilteringSpanProcessor::OnEnd():
if (DiscardScope::isActive())
return; // drop the span
@endcode
@note Thread safety: the flag is thread-local, so each thread observes only
its own discard signal — no synchronization is required.
@see SpanGuard::discard(), FilteringSpanProcessor (Telemetry.cpp)
*/
/**
* Thread-local discard signaling between SpanGuard and the span processor.
*
* SpanGuard::discard() wants to drop a span without sending it to the
* exporter. The OTel SDK calls SpanProcessor::OnEnd() synchronously on the
* same thread that calls Span::End(), so a thread-local flag set just before
* End() and read inside OnEnd() lets FilteringSpanProcessor drop the span
* before it enters the batch export queue.
*
* This side-channel avoids inspecting the Recordable's internals (which vary
* by exporter type — SpanData vs OtlpRecordable).
*
* The flag is a *private* thread-local member of DiscardScope, mutated only by
* its constructor and destructor. This gives real access control rather than a
* naming convention: no code that includes this header can flip the flag
* directly — it can only enter a DiscardScope (which sets and clears the flag
* over its own lifetime) and observe the state via DiscardScope::isActive().
* Binding set/clear to a scope also means the flag cannot leak onto the next
* span even if End() were to throw.
*
* Kept in a separate header to avoid transitive include bloat: SpanGuard.h
* only needs this signaling, not the full Telemetry.h with BasicConfig/Journal.
*
* Usage:
* @code
* // In SpanGuard::discard():
* {
* DiscardScope discardScope; // flag set for this scope only
* span->End(); // OnEnd() runs synchronously, sees flag
* } // flag cleared here, unconditionally
*
* // In FilteringSpanProcessor::OnEnd():
* if (DiscardScope::isActive())
* return; // drop the span
* @endcode
*
* @note Thread safety: the flag is thread-local, so each thread observes only
* its own discard signal — no synchronization is required.
*
* @see SpanGuard::discard(), FilteringSpanProcessor (Telemetry.cpp)
*/
namespace xrpl::telemetry {
/** RAII guard that marks the current thread's span for discard.
Sets the thread-local discard flag on construction and clears it on
destruction, so a span ended within the guard's scope is dropped by
FilteringSpanProcessor::OnEnd() while the flag stays confined to that scope.
The flag is private and mutated only here, so no other code can set it.
Non-copyable and non-movable — its sole purpose is the scoped flag lifetime.
*/
/**
* RAII guard that marks the current thread's span for discard.
*
* Sets the thread-local discard flag on construction and clears it on
* destruction, so a span ended within the guard's scope is dropped by
* FilteringSpanProcessor::OnEnd() while the flag stays confined to that scope.
* The flag is private and mutated only here, so no other code can set it.
* Non-copyable and non-movable — its sole purpose is the scoped flag lifetime.
*/
class DiscardScope
{
public:
@@ -71,9 +73,11 @@ public:
DiscardScope&
operator=(DiscardScope&&) = delete;
/** @return true if the current thread is inside a DiscardScope, i.e. the
span ending now should be dropped rather than exported. Read by
FilteringSpanProcessor::OnEnd(). */
/**
* @return true if the current thread is inside a DiscardScope, i.e. the
* span ending now should be dropped rather than exported. Read by
* FilteringSpanProcessor::OnEnd().
*/
[[nodiscard]] static bool
isActive() noexcept
{
@@ -81,8 +85,10 @@ public:
}
private:
/** Thread-local discard flag. Private, so only this class's ctor/dtor can
mutate it; observers use isActive(). One instance per thread. */
/**
* Thread-local discard flag. Private, so only this class's ctor/dtor can
* mutate it; observers use isActive(). One instance per thread.
*/
inline static thread_local bool discarding = false;
};

View File

@@ -1,63 +1,65 @@
#pragma once
/** Account-address redaction for telemetry span attributes.
Path-finding RPC handlers would otherwise emit the caller's raw
account addresses as span attributes. To keep plaintext addresses out
of the telemetry backend, they are hashed at the point of emission.
This header exposes a single pure helper that turns an address into a
short, stable, obfuscated token.
Data flow:
handler -> redactAccount(addr) -> span attribute -> OTLP export
The returned token is the first 16 hex characters (lowercase) of the
SHA-512Half digest of the address. It is deterministic (same address
always maps to the same token) so operators can still correlate spans
for a given account across nodes and restarts.
The hash is unsalted, so it is obfuscation, not a secrecy guarantee:
XRP account addresses are a public, enumerable set, so a determined
observer with the telemetry stream could rebuild the address->token
mapping. The goal here is to keep plaintext addresses out of traces
and dashboards, not to defend against a precomputation attack. A salt
is intentionally omitted because it would break cross-node/restart
correlation, which is the reason for hashing rather than dropping.
A second, independent hashing layer runs in the OpenTelemetry
Collector (an `attributes/hash` processor) as defense-in-depth for
any node that emits a raw value.
@note This function is pure and reentrant: it holds no global state,
performs no I/O, and is safe to call concurrently from any thread.
Usage example:
@code
#include <xrpl/telemetry/Redaction.h>
using namespace xrpl::telemetry;
span.setAttribute(
pathfind_span::attr::sourceAccount, redactAccount(src.asString()));
@endcode
Edge case (empty input yields empty output):
@code
assert(redactAccount("") == "");
@endcode
*/
/**
* Account-address redaction for telemetry span attributes.
*
* Path-finding RPC handlers would otherwise emit the caller's raw
* account addresses as span attributes. To keep plaintext addresses out
* of the telemetry backend, they are hashed at the point of emission.
* This header exposes a single pure helper that turns an address into a
* short, stable, obfuscated token.
*
* Data flow:
*
* handler -> redactAccount(addr) -> span attribute -> OTLP export
*
* The returned token is the first 16 hex characters (lowercase) of the
* SHA-512Half digest of the address. It is deterministic (same address
* always maps to the same token) so operators can still correlate spans
* for a given account across nodes and restarts.
*
* The hash is unsalted, so it is obfuscation, not a secrecy guarantee:
* XRP account addresses are a public, enumerable set, so a determined
* observer with the telemetry stream could rebuild the address->token
* mapping. The goal here is to keep plaintext addresses out of traces
* and dashboards, not to defend against a precomputation attack. A salt
* is intentionally omitted because it would break cross-node/restart
* correlation, which is the reason for hashing rather than dropping.
*
* A second, independent hashing layer runs in the OpenTelemetry
* Collector (an `attributes/hash` processor) as defense-in-depth for
* any node that emits a raw value.
*
* @note This function is pure and reentrant: it holds no global state,
* performs no I/O, and is safe to call concurrently from any thread.
*
* Usage example:
* @code
* #include <xrpl/telemetry/Redaction.h>
* using namespace xrpl::telemetry;
*
* span.setAttribute(
* pathfind_span::attr::sourceAccount, redactAccount(src.asString()));
* @endcode
*
* Edge case (empty input yields empty output):
* @code
* assert(redactAccount("") == "");
* @endcode
*/
#include <string>
#include <string_view>
namespace xrpl::telemetry {
/** Hash an account address into a short, stable, obfuscated token.
@param addr The account address to redact (e.g. an r-address).
@return The first 16 lowercase hex characters of sha512Half(addr),
or an empty string when @p addr is empty.
*/
/**
* Hash an account address into a short, stable, obfuscated token.
*
* @param addr The account address to redact (e.g. an r-address).
* @return The first 16 lowercase hex characters of sha512Half(addr),
* or an empty string when @p addr is empty.
*/
[[nodiscard]] std::string
redactAccount(std::string_view addr);

View File

@@ -1,18 +1,19 @@
#pragma once
/** Compile-time string concatenation utility and shared telemetry constants.
/**
* Compile-time string concatenation utility and shared telemetry constants.
*
* Provides StaticStr<N> — a compile-time string buffer that implicitly
* converts to std::string_view — and join() for dot-separated concatenation.
* Module-specific span names (e.g. RPC, consensus) live in their respective
* modules and build upon these shared primitives.
*
* @note These constants are NOT guarded by XRPL_ENABLE_TELEMETRY because
* @note These constants are NOT guarded by XRPL_ENABLE_TELEMETRY because
* call sites reference them even when SpanGuard methods are no-ops
* (the no-op stubs still accept string_view parameters). The compiler
* elides all inline constexpr values whose only uses are in dead code.
*
* @note Json::StaticString (jss.h) is a pointer wrapper without
* @note Json::StaticString (jss.h) is a pointer wrapper without
* concatenation support. boost::static_string is not constexpr.
* StaticStr<N> exists specifically for compile-time dot-join composition.
*
@@ -37,8 +38,10 @@ namespace xrpl::telemetry {
// ===== Compile-time string utility =========================================
/// Fixed-size character buffer for compile-time string operations.
/// Implicitly converts to std::string_view at zero cost.
/**
* Fixed-size character buffer for compile-time string operations.
* Implicitly converts to std::string_view at zero cost.
*/
template <std::size_t N>
struct StaticStr
{
@@ -60,11 +63,15 @@ struct StaticStr
}
};
/// Deduction guide: StaticStr from string literal.
/**
* Deduction guide: StaticStr from string literal.
*/
template <std::size_t N>
StaticStr(char const (&)[N]) -> StaticStr<N - 1>;
/// Create a StaticStr from a string literal.
/**
* Create a StaticStr from a string literal.
*/
template <std::size_t N>
constexpr auto
makeStr(char const (&str)[N])
@@ -72,7 +79,9 @@ makeStr(char const (&str)[N])
return StaticStr<N - 1>(str);
}
/// Concatenate two StaticStr values with a dot separator.
/**
* Concatenate two StaticStr values with a dot separator.
*/
template <std::size_t A, std::size_t B>
constexpr auto
join(StaticStr<A> const& lhs, StaticStr<B> const& rhs)

View File

@@ -1,90 +1,91 @@
#pragma once
/** Abstract interface for OpenTelemetry distributed tracing.
Provides the Telemetry base class that all components use to create trace
spans. Three concrete implementations exist, selected at construction time
by makeTelemetry():
- 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.
- NullTelemetryOtel (Telemetry.cpp): no-op stub that still depends on
the OTel API (used during transition or for testing).
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. Root span at a subsystem entry point (typical usage):
@code
#include <xrpld/rpc/detail/RpcSpanNames.h>
using namespace xrpl::telemetry;
// In an RPC handler dispatch:
auto guard = SpanGuard::span(
TraceCategory::Rpc, rpc_span::prefix::command, commandName);
guard.setAttribute(rpc_span::attr::command, commandName);
// ... process request
// guard destructor automatically ends the span on scope exit
@endcode
2. Child span for a sub-operation (scoped child):
@code
auto parent = SpanGuard::span(
TraceCategory::Rpc, rpc_span::prefix::rpc, rpc_span::op::process);
{
auto child = parent.childSpan(rpc_span::op::process);
child.setAttribute(rpc_span::attr::version, apiVersion);
// child ends here
}
@endcode
3. Unrelated span (cross-scope, same thread):
@code
// gRPC and RPC handlers can be active simultaneously
auto grpcSpan = SpanGuard::span(
TraceCategory::Rpc, grpc_span::prefix::grpc, grpc_span::attr::method);
auto rpcSpan = SpanGuard::span(
TraceCategory::Rpc, rpc_span::prefix::command, commandName);
// both spans end on scope exit
@endcode
4. Cross-thread context propagation:
@code
// Thread A: capture the active context while span is in scope
auto ctx = parentGuard.captureContext();
// Thread B: create child span with explicit parent
auto child = SpanGuard::childSpan(rpc_span::op::process, 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.
*/
/**
* Abstract interface for OpenTelemetry distributed tracing.
*
* Provides the Telemetry base class that all components use to create trace
* spans. Three concrete implementations exist, selected at construction time
* by makeTelemetry():
*
* - 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.
* - NullTelemetryOtel (Telemetry.cpp): no-op stub that still depends on
* the OTel API (used during transition or for testing).
*
* 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. Root span at a subsystem entry point (typical usage):
* @code
* #include <xrpld/rpc/detail/RpcSpanNames.h>
* using namespace xrpl::telemetry;
*
* // In an RPC handler dispatch:
* auto guard = SpanGuard::span(
* TraceCategory::Rpc, rpc_span::prefix::command, commandName);
* guard.setAttribute(rpc_span::attr::command, commandName);
* // ... process request
* // guard destructor automatically ends the span on scope exit
* @endcode
*
* 2. Child span for a sub-operation (scoped child):
* @code
* auto parent = SpanGuard::span(
* TraceCategory::Rpc, rpc_span::prefix::rpc, rpc_span::op::process);
* {
* auto child = parent.childSpan(rpc_span::op::process);
* child.setAttribute(rpc_span::attr::version, apiVersion);
* // child ends here
* }
* @endcode
*
* 3. Unrelated span (cross-scope, same thread):
* @code
* // gRPC and RPC handlers can be active simultaneously
* auto grpcSpan = SpanGuard::span(
* TraceCategory::Rpc, grpc_span::prefix::grpc, grpc_span::attr::method);
* auto rpcSpan = SpanGuard::span(
* TraceCategory::Rpc, rpc_span::prefix::command, commandName);
* // both spans end on scope exit
* @endcode
*
* 4. Cross-thread context propagation:
* @code
* // Thread A: capture the active context while span is in scope
* auto ctx = parentGuard.captureContext();
*
* // Thread B: create child span with explicit parent
* auto child = SpanGuard::childSpan(rpc_span::op::process, 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/beast/utility/Journal.h>
#include <xrpl/config/BasicConfig.h>
@@ -107,129 +108,171 @@
namespace xrpl::telemetry {
#ifdef XRPL_ENABLE_TELEMETRY
/** OTel instrumentation scope (tracer) name. Identifies this library as the
source of spans; distinct from the `service.name` resource attribute
(Setup::serviceName), which is config-overridable. */
/**
* OTel instrumentation scope (tracer) name. Identifies this library as the
* source of spans; distinct from the `service.name` resource attribute
* (Setup::serviceName), which is config-overridable.
*/
inline constexpr std::string_view kTracerName{"xrpld"};
#endif
class Telemetry
{
/** Global singleton pointer, set by start()/stop() in the active
implementation. Allows SpanGuard factory methods to access the
Telemetry instance without callers passing it explicitly.
Atomic with acquire/release ordering: start()/stop() store on
the initialization thread, factory methods load on worker threads.
@see setInstance(), getInstance()
*/
/**
* Global singleton pointer, set by start()/stop() in the active
* implementation. Allows SpanGuard factory methods to access the
* Telemetry instance without callers passing it explicitly.
*
* Atomic with acquire/release ordering: start()/stop() store on
* the initialization thread, factory methods load on worker threads.
* @see setInstance(), getInstance()
*/
inline static std::atomic<Telemetry*> instance{nullptr};
public:
/** Get the global Telemetry instance.
@return Pointer to the active instance, or nullptr if not started.
*/
/**
* Get the global Telemetry instance.
* @return Pointer to the active instance, or nullptr if not started.
*/
static Telemetry*
getInstance()
{
return instance.load(std::memory_order_acquire);
}
/** Set the global Telemetry instance.
Called by start()/stop() in concrete implementations.
Tests can call this with a mock to override the global instance.
@param t Pointer to the Telemetry instance, or nullptr to clear.
*/
/**
* Set the global Telemetry instance.
* Called by start()/stop() in concrete implementations.
* Tests can call this with a mock to override the global instance.
* @param t Pointer to the Telemetry instance, or nullptr to clear.
*/
static void
setInstance(Telemetry* t)
{
instance.store(t, std::memory_order_release);
}
/** Configuration parsed from the [telemetry] section of xrpld.cfg.
All fields have sensible defaults so the section can be minimal
or omitted entirely. See TelemetryConfig.cpp for the parser.
*/
/**
* Configuration parsed from the [telemetry] section of xrpld.cfg.
*
* All fields have sensible defaults so the section can be minimal
* or omitted entirely. See TelemetryConfig.cpp for the parser.
*/
struct Setup
{
/** Master switch: true to enable tracing at runtime. */
/**
* Master switch: true to enable tracing at runtime.
*/
bool enabled = false;
/** OTel resource attribute `service.name`. */
/**
* OTel resource attribute `service.name`.
*/
std::string serviceName = "xrpld";
/** OTel resource attribute `service.version` (set from BuildInfo). */
/**
* OTel resource attribute `service.version` (set from BuildInfo).
*/
std::string serviceVersion;
/** OTel resource attribute `service.instance.id` (defaults to node
public key). */
/**
* OTel resource attribute `service.instance.id` (defaults to node
* public key).
*/
std::string serviceInstanceId;
/** OTLP/HTTP endpoint URL where spans are sent. */
/**
* OTLP/HTTP endpoint URL where spans are sent.
*/
std::string exporterEndpoint = "http://localhost:4318/v1/traces";
/** Whether to use TLS for the exporter connection. */
/**
* Whether to use TLS for the exporter connection.
*/
bool useTls = false;
/** Path to a CA certificate bundle for TLS verification. */
/**
* Path to a CA certificate bundle for TLS verification.
*/
std::string tlsCertPath;
/** Head-based sampling ratio. Intentionally fixed at 1.0 (sample
everything) and NOT read from config. A per-node ratio would let
nodes make divergent keep/drop decisions for the same distributed
trace, producing broken/partial traces. The ratio sampler is wrapped
in a ParentBasedSampler (see Telemetry.cpp) so spans inheriting a
remote parent honor the upstream sampled flag. Volume reduction is
delegated to the collector's tail sampling; for node-local post-hoc
dropping see SpanGuard::discard().
*/
/**
* Head-based sampling ratio. Intentionally fixed at 1.0 (sample
* everything) and NOT read from config. A per-node ratio would let
* nodes make divergent keep/drop decisions for the same distributed
* trace, producing broken/partial traces. The ratio sampler is wrapped
* in a ParentBasedSampler (see Telemetry.cpp) so spans inheriting a
* remote parent honor the upstream sampled flag. Volume reduction is
* delegated to the collector's tail sampling; for node-local post-hoc
* dropping see SpanGuard::discard().
*/
static constexpr double samplingRatio = 1.0;
/** Maximum number of spans per batch export. */
/**
* Maximum number of spans per batch export.
*/
std::uint32_t batchSize = 512;
/** Delay between batch exports. */
/**
* Delay between batch exports.
*/
std::chrono::milliseconds batchDelay = std::chrono::milliseconds{5000};
/** Maximum number of spans queued before dropping. */
/**
* Maximum number of spans queued before dropping.
*/
std::uint32_t maxQueueSize = 2048;
/** Network identifier, added as an OTel resource attribute. */
/**
* Network identifier, added as an OTel resource attribute.
*/
std::uint32_t networkId = 0;
/** Network type label (e.g. "mainnet", "testnet", "devnet"). */
/**
* Network type label (e.g. "mainnet", "testnet", "devnet").
*/
std::string networkType = "mainnet";
/** Enable tracing for transaction processing. */
/**
* Enable tracing for transaction processing.
*/
bool traceTransactions = true;
/** Enable tracing for consensus rounds. */
/**
* Enable tracing for consensus rounds.
*/
bool traceConsensus = true;
/** Enable tracing for RPC request handling. */
/**
* Enable tracing for RPC request handling.
*/
bool traceRpc = true;
/** Enable tracing for peer-to-peer messages (enabled by default;
high volume). */
/**
* Enable tracing for peer-to-peer messages (enabled by default;
* high volume).
*/
bool tracePeer = true;
/** Enable tracing for ledger close/accept. */
/**
* Enable tracing for ledger close/accept.
*/
bool traceLedger = true;
};
virtual ~Telemetry() = default;
/** Update the service instance ID (OTel resource attribute
`service.instance.id`).
Must be called before start(). The node public key is not available
when Telemetry is constructed (during the ApplicationImp member
initializer list), so this setter allows Application::setup() to
inject the identity once nodeIdentity_ is known.
@param id The node's base58-encoded public key or custom identifier.
*/
/**
* Update the service instance ID (OTel resource attribute
* `service.instance.id`).
*
* Must be called before start(). The node public key is not available
* when Telemetry is constructed (during the ApplicationImp member
* initializer list), so this setter allows Application::setup() to
* inject the identity once nodeIdentity_ is known.
*
* @param id The node's base58-encoded public key or custom identifier.
*/
virtual void
setServiceInstanceId(std::string const& id)
{
@@ -237,80 +280,97 @@ public:
(void)id;
}
/** Initialize the tracing pipeline (exporter, processor, provider).
Call after construction.
*/
/**
* Initialize the tracing pipeline (exporter, processor, provider).
* Call after construction.
*/
virtual void
start() = 0;
/** Flush pending spans and shut down the tracing pipeline.
Call before destruction.
*/
/**
* Flush pending spans and shut down the tracing pipeline.
* Call before destruction.
*/
virtual void
stop() = 0;
/** @return true if this instance is actively exporting spans. */
/**
* @return true if this instance is actively exporting spans.
*/
[[nodiscard]] virtual bool
isEnabled() const = 0;
/** @return true if transaction processing should be traced. */
/**
* @return true if transaction processing should be traced.
*/
[[nodiscard]] virtual bool
shouldTraceTransactions() const = 0;
/** @return true if consensus rounds should be traced. */
/**
* @return true if consensus rounds should be traced.
*/
[[nodiscard]] virtual bool
shouldTraceConsensus() const = 0;
/** @return true if RPC request handling should be traced. */
/**
* @return true if RPC request handling should be traced.
*/
[[nodiscard]] virtual bool
shouldTraceRpc() const = 0;
/** @return true if peer-to-peer messages should be traced. */
/**
* @return true if peer-to-peer messages should be traced.
*/
[[nodiscard]] virtual bool
shouldTracePeer() const = 0;
/** @return true if ledger close/accept should be traced. */
/**
* @return true if ledger close/accept should be traced.
*/
[[nodiscard]] virtual bool
shouldTraceLedger() const = 0;
#ifdef XRPL_ENABLE_TELEMETRY
/** Get or create a named tracer instance.
@param name Tracer name used to identify the instrumentation library.
@return A shared pointer to the Tracer.
*/
/**
* Get or create a named tracer instance.
*
* @param name Tracer name used to identify the instrumentation library.
* @return A shared pointer to the Tracer.
*/
virtual opentelemetry::nostd::shared_ptr<opentelemetry::trace::Tracer>
getTracer(std::string_view name = kTracerName) = 0;
/** Start a new span on the current thread's context.
The span becomes a child of the current active span (if any) via
OpenTelemetry's context propagation.
@param name Span name (typically "rpc.command.<cmd>").
@param kind The span kind (defaults to kInternal). Possible values:
- kInternal: default, in-process operation
- kServer: incoming synchronous request (e.g. RPC)
- kClient: outgoing synchronous request
- kProducer: async message send (e.g. peer broadcast)
- kConsumer: async message receive
@return A shared pointer to the new Span.
*/
/**
* Start a new span on the current thread's context.
*
* The span becomes a child of the current active span (if any) via
* OpenTelemetry's context propagation.
*
* @param name Span name (typically "rpc.command.<cmd>").
* @param kind The span kind (defaults to kInternal). Possible values:
* - kInternal: default, in-process operation
* - kServer: incoming synchronous request (e.g. RPC)
* - kClient: outgoing synchronous request
* - kProducer: async message send (e.g. peer broadcast)
* - kConsumer: async message receive
* @return A shared pointer to the new Span.
*/
virtual opentelemetry::nostd::shared_ptr<opentelemetry::trace::Span>
startSpan(
std::string_view name,
opentelemetry::trace::SpanKind kind = opentelemetry::trace::SpanKind::kInternal) = 0;
/** Start a new span with an explicit parent context.
Use this overload when the parent span is not on the current
thread's context stack (e.g. cross-thread trace propagation).
@param name Span name.
@param parentContext The parent span's context.
@param kind The span kind (defaults to kInternal).
@return A shared pointer to the new Span.
*/
/**
* Start a new span with an explicit parent context.
*
* Use this overload when the parent span is not on the current
* thread's context stack (e.g. cross-thread trace propagation).
*
* @param name Span name.
* @param parentContext The parent span's context.
* @param kind The span kind (defaults to kInternal).
* @return A shared pointer to the new Span.
*/
virtual opentelemetry::nostd::shared_ptr<opentelemetry::trace::Span>
startSpan(
std::string_view name,
@@ -319,26 +379,28 @@ public:
#endif
};
/** Create a Telemetry instance.
Returns a TelemetryImpl when setup.enabled is true, or a
NullTelemetry no-op stub otherwise.
@param setup Configuration from the [telemetry] config section.
@param journal Journal for log output during initialization.
*/
/**
* Create a Telemetry instance.
*
* Returns a TelemetryImpl when setup.enabled is true, or a
* NullTelemetry no-op stub otherwise.
*
* @param setup Configuration from the [telemetry] config section.
* @param journal Journal for log output during initialization.
*/
std::unique_ptr<Telemetry>
makeTelemetry(Telemetry::Setup const& setup, beast::Journal journal);
/** Parse the [telemetry] config section into a Setup struct.
@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.
*/
/**
* Parse the [telemetry] config section into a Setup struct.
*
* @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
makeTelemetrySetup(
Section const& section,

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