refactor(telemetry): replace per-category factory methods with TraceCategory enum

Replace rpcSpan(), txSpan(), consensusSpan(), peerSpan(), ledgerSpan()
with a single span(TraceCategory, prefix, name) factory method. Adding
a new traceable subsystem now requires only a new enum value and one
switch case — no new methods or header changes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Pratik Mankawde
2026-04-17 17:40:37 +01:00
parent ad233846fa
commit 7c8a3906a1
2 changed files with 82 additions and 112 deletions

View File

@@ -9,27 +9,23 @@
Dependency diagram:
+-----------------------------------------+
| SpanGuard |
+-----------------------------------------+
| - impl_ : unique_ptr<Impl> (pimpl) |
+-----------------------------------------+
| + rpcSpan(name) : SpanGuard [static] |
| + txSpan(name) : SpanGuard [static] |
| + consensusSpan(name) [static] |
| + peerSpan(name) [static] |
| + ledgerSpan(name) [static] |
| + span(name) [static] |
| + childSpan(name) : SpanGuard |
| + linkedSpan(name) : SpanGuard |
| + captureContext() : SpanContext |
| + setAttribute(key, value) |
| + setOk() / setError(desc) |
| + addEvent(name) |
| + recordException(e) |
| + discard() |
| + operator bool() |
+-----------------------------------------+
+-------------------------------------------+
| SpanGuard |
+-------------------------------------------+
| - impl_ : unique_ptr<Impl> (pimpl) |
+-------------------------------------------+
| + span(name) : SpanGuard [static] |
| + span(cat, prefix, name) [static] |
| + childSpan(name) : SpanGuard |
| + linkedSpan(name) : SpanGuard |
| + captureContext() : SpanContext |
| + setAttribute(key, value) |
| + setOk() / setError(desc) |
| + addEvent(name) |
| + recordException(e) |
| + discard() |
| + operator bool() |
+-------------------------------------------+
| hides (pimpl)
+-------+-------+
| |
@@ -47,9 +43,14 @@
Usage examples:
1. Basic RPC tracing (factory method):
1. Basic RPC tracing (factory method with category):
@code
auto span = SpanGuard::rpcSpan("rpc.command.submit");
// Define prefix at class level:
static constexpr std::string_view spanPrefix_ = "rpc.command";
// At the call site:
auto span = SpanGuard::span(
TraceCategory::Rpc, spanPrefix_, "submit");
span.setAttribute("xrpl.rpc.command", "submit");
span.setAttribute("xrpl.rpc.status", "success");
// span ended automatically on scope exit
@@ -57,7 +58,8 @@
2. Error recording:
@code
auto span = SpanGuard::rpcSpan("rpc.command.submit");
auto span = SpanGuard::span(
TraceCategory::Rpc, "rpc.command", "submit");
try {
doWork();
span.setOk();
@@ -69,7 +71,8 @@
3. Cross-thread context propagation:
@code
// Thread A: create span and capture context
auto span = SpanGuard::consensusSpan("consensus.round");
auto span = SpanGuard::span(
TraceCategory::Consensus, "consensus", "round");
auto ctx = span.captureContext();
// Thread B: create child with captured context
@@ -78,7 +81,8 @@
4. Conditional check (rarely needed — methods are no-ops on null):
@code
auto span = SpanGuard::rpcSpan("rpc.request");
auto span = SpanGuard::span(
TraceCategory::Rpc, "rpc", "request");
if (span) {
// expensive attribute computation only when active
span.setAttribute("xrpl.rpc.payload_size", computeSize());
@@ -87,7 +91,8 @@
5. Tail-based filtering via discard():
@code
auto span = SpanGuard::txSpan("tx.process");
auto span = SpanGuard::span(
TraceCategory::Transactions, "tx", "process");
auto result = preflight(tx);
if (result != tesSUCCESS) {
span.discard(); // drop span, never exported
@@ -119,6 +124,14 @@
namespace xrpl {
namespace telemetry {
/** Trace subsystem categories for conditional span creation.
Each value maps to a runtime config flag (e.g. `trace_rpc=1`).
Used by SpanGuard::span(TraceCategory, prefix, name) to decide
whether to create a real span or return a null guard.
*/
enum class TraceCategory { Rpc, Transactions, Consensus, Peer, Ledger };
/** Opaque wrapper for an OTel context snapshot.
Used to propagate trace context across threads. Created by
@@ -180,32 +193,22 @@ public:
operator=(SpanGuard const&) = delete;
// --- Static factory methods ----------------------------------------
// Each checks the global Telemetry instance and the corresponding
// shouldTrace*() flag. Returns a null guard if tracing is off.
/** Create an unconditional span (always created if telemetry is on). */
/** Create an unconditional span (always created if telemetry is on).
@param name Full span name (e.g. "app.startup").
*/
static SpanGuard
span(std::string_view name);
/** Create a span guarded by shouldTraceRpc(). */
/** Create a span guarded by a TraceCategory flag.
The span name is built as "prefix.name". Returns a null guard
if the category is disabled in config.
@param cat Trace subsystem category.
@param prefix Span name prefix (e.g. "rpc.command").
@param name Span name suffix (e.g. "submit").
*/
static SpanGuard
rpcSpan(std::string_view name);
/** Create a span guarded by shouldTraceTransactions(). */
static SpanGuard
txSpan(std::string_view name);
/** Create a span guarded by shouldTraceConsensus(). */
static SpanGuard
consensusSpan(std::string_view name);
/** Create a span guarded by shouldTracePeer(). */
static SpanGuard
peerSpan(std::string_view name);
/** Create a span guarded by shouldTraceLedger(). */
static SpanGuard
ledgerSpan(std::string_view name);
span(TraceCategory cat, std::string_view prefix, std::string_view name);
// --- Child / linked span creation ----------------------------------
@@ -332,27 +335,7 @@ public:
return {};
}
static SpanGuard
rpcSpan(std::string_view)
{
return {};
}
static SpanGuard
txSpan(std::string_view)
{
return {};
}
static SpanGuard
consensusSpan(std::string_view)
{
return {};
}
static SpanGuard
peerSpan(std::string_view)
{
return {};
}
static SpanGuard
ledgerSpan(std::string_view)
span(TraceCategory, std::string_view, std::string_view)
{
return {};
}

View File

@@ -4,10 +4,10 @@
The public SpanGuard.h header contains only standard-library types
and forward-declares the Impl struct.
Static factory methods (rpcSpan, txSpan, etc.) access the global
Telemetry instance via Telemetry::getInstance(), check the relevant
shouldTrace*() flag, and return either an active guard with a real
Span+Scope or a null guard whose methods are all no-ops.
Static factory methods access the global Telemetry instance via
Telemetry::getInstance(), check whether the requested TraceCategory
is enabled, and return either an active guard with a real Span+Scope
or a null guard whose methods are all no-ops.
The Impl struct holds the OTel Span (shared_ptr) and Scope.
Scope is non-movable, but since Impl lives behind a unique_ptr,
@@ -109,6 +109,28 @@ operator bool() const
// ===== Static factory methods ==============================================
/** Check whether the given TraceCategory is enabled on the Telemetry instance.
@return true if the category's shouldTrace*() flag is on.
*/
static bool
isCategoryEnabled(Telemetry const& tel, TraceCategory cat)
{
switch (cat)
{
case TraceCategory::Rpc:
return tel.shouldTraceRpc();
case TraceCategory::Transactions:
return tel.shouldTraceTransactions();
case TraceCategory::Consensus:
return tel.shouldTraceConsensus();
case TraceCategory::Peer:
return tel.shouldTracePeer();
case TraceCategory::Ledger:
return tel.shouldTraceLedger();
}
return false; // unreachable, silences compiler warning
}
SpanGuard
SpanGuard::span(std::string_view name)
{
@@ -119,48 +141,13 @@ SpanGuard::span(std::string_view name)
}
SpanGuard
SpanGuard::rpcSpan(std::string_view name)
SpanGuard::span(TraceCategory cat, std::string_view prefix, std::string_view name)
{
auto* tel = Telemetry::getInstance();
if (!tel || !tel->isEnabled() || !tel->shouldTraceRpc())
if (!tel || !tel->isEnabled() || !isCategoryEnabled(*tel, cat))
return {};
return SpanGuard(std::make_unique<Impl>(tel->startSpan(name)));
}
SpanGuard
SpanGuard::txSpan(std::string_view name)
{
auto* tel = Telemetry::getInstance();
if (!tel || !tel->isEnabled() || !tel->shouldTraceTransactions())
return {};
return SpanGuard(std::make_unique<Impl>(tel->startSpan(name)));
}
SpanGuard
SpanGuard::consensusSpan(std::string_view name)
{
auto* tel = Telemetry::getInstance();
if (!tel || !tel->isEnabled() || !tel->shouldTraceConsensus())
return {};
return SpanGuard(std::make_unique<Impl>(tel->startSpan(name)));
}
SpanGuard
SpanGuard::peerSpan(std::string_view name)
{
auto* tel = Telemetry::getInstance();
if (!tel || !tel->isEnabled() || !tel->shouldTracePeer())
return {};
return SpanGuard(std::make_unique<Impl>(tel->startSpan(name)));
}
SpanGuard
SpanGuard::ledgerSpan(std::string_view name)
{
auto* tel = Telemetry::getInstance();
if (!tel || !tel->isEnabled() || !tel->shouldTraceLedger())
return {};
return SpanGuard(std::make_unique<Impl>(tel->startSpan(name)));
auto fullName = std::string(prefix) + "." + std::string(name);
return SpanGuard(std::make_unique<Impl>(tel->startSpan(fullName)));
}
// ===== Child / linked span creation ========================================