Skip to content

Commit b5fb71c

Browse files
Phase 1c: RPC layer telemetry integration
Add tracing instrumentation to the RPC request handling layer: TracingInstrumentation.h: - Convenience macros (XRPL_TRACE_RPC, XRPL_TRACE_TX, etc.) that create RAII SpanGuard objects when telemetry is enabled - XRPL_TRACE_SET_ATTR / XRPL_TRACE_EXCEPTION for span enrichment - Zero-overhead no-ops when XRPL_ENABLE_TELEMETRY is not defined RPCHandler.cpp: - Trace each RPC command with span name "rpc.command.<method>" - Record command name, API version, role, and status as attributes - Capture exceptions on the span for error visibility ServerHandler.cpp: - Trace HTTP requests ("rpc.request"), WebSocket messages ("rpc.ws_message"), and processRequest ("rpc.process") Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 252a4bb commit b5fb71c

File tree

4 files changed

+131
-0
lines changed

4 files changed

+131
-0
lines changed

.github/scripts/levelization/results/ordering.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ xrpld.perflog > xrpl.json
259259
xrpld.rpc > xrpl.basics
260260
xrpld.rpc > xrpl.core
261261
xrpld.rpc > xrpld.core
262+
xrpld.rpc > xrpld.telemetry
262263
xrpld.rpc > xrpl.json
263264
xrpld.rpc > xrpl.ledger
264265
xrpld.rpc > xrpl.net
@@ -269,3 +270,4 @@ xrpld.rpc > xrpl.resource
269270
xrpld.rpc > xrpl.server
270271
xrpld.rpc > xrpl.tx
271272
xrpld.shamap > xrpl.shamap
273+
xrpld.telemetry > xrpl.telemetry

src/xrpld/rpc/detail/RPCHandler.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <xrpld/rpc/Role.h>
99
#include <xrpld/rpc/detail/Handler.h>
1010
#include <xrpld/rpc/detail/Tuning.h>
11+
#include <xrpld/telemetry/TracingInstrumentation.h>
1112

1213
#include <xrpl/basics/Log.h>
1314
#include <xrpl/core/JobQueue.h>
@@ -157,6 +158,11 @@ template <class Object, class Method>
157158
Status
158159
callMethod(JsonContext& context, Method method, std::string const& name, Object& result)
159160
{
161+
XRPL_TRACE_RPC(context.app.getTelemetry(), "rpc.command." + name);
162+
XRPL_TRACE_SET_ATTR("xrpl.rpc.command", name.c_str());
163+
XRPL_TRACE_SET_ATTR("xrpl.rpc.version", static_cast<int64_t>(context.apiVersion));
164+
XRPL_TRACE_SET_ATTR("xrpl.rpc.role", (context.role == Role::ADMIN ? "admin" : "user"));
165+
160166
static std::atomic<std::uint64_t> requestId{0};
161167
auto& perfLog = context.app.getPerfLog();
162168
std::uint64_t const curId = ++requestId;
@@ -172,12 +178,15 @@ callMethod(JsonContext& context, Method method, std::string const& name, Object&
172178
JLOG(context.j.debug()) << "RPC call " << name << " completed in "
173179
<< ((end - start).count() / 1000000000.0) << "seconds";
174180
perfLog.rpcFinish(name, curId);
181+
XRPL_TRACE_SET_ATTR("xrpl.rpc.status", "success");
175182
return ret;
176183
}
177184
catch (std::exception& e)
178185
{
179186
perfLog.rpcError(name, curId);
180187
JLOG(context.j.info()) << "Caught throw: " << e.what();
188+
XRPL_TRACE_EXCEPTION(e);
189+
XRPL_TRACE_SET_ATTR("xrpl.rpc.status", "error");
181190

182191
if (context.loadType == Resource::feeReferenceRPC)
183192
context.loadType = Resource::feeExceptionRPC;

src/xrpld/rpc/detail/ServerHandler.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <xrpld/rpc/detail/Tuning.h>
88
#include <xrpld/rpc/detail/WSInfoSub.h>
99
#include <xrpld/rpc/json_body.h>
10+
#include <xrpld/telemetry/TracingInstrumentation.h>
1011

1112
#include <xrpl/basics/Log.h>
1213
#include <xrpl/basics/base64.h>
@@ -267,6 +268,8 @@ buffers_to_string(ConstBufferSequence const& bs)
267268
void
268269
ServerHandler::onRequest(Session& session)
269270
{
271+
XRPL_TRACE_RPC(app_.getTelemetry(), "rpc.request");
272+
270273
// Make sure RPC is enabled on the port
271274
if (session.port().protocol.count("http") == 0 && session.port().protocol.count("https") == 0)
272275
{
@@ -378,6 +381,7 @@ ServerHandler::processSession(
378381
std::shared_ptr<JobQueue::Coro> const& coro,
379382
Json::Value const& jv)
380383
{
384+
XRPL_TRACE_RPC(app_.getTelemetry(), "rpc.ws_message");
381385
auto is = std::static_pointer_cast<WSInfoSub>(session->appDefined);
382386
if (is->getConsumer().disconnect(m_journal))
383387
{
@@ -566,6 +570,7 @@ ServerHandler::processRequest(
566570
std::string_view forwardedFor,
567571
std::string_view user)
568572
{
573+
XRPL_TRACE_RPC(app_.getTelemetry(), "rpc.process");
569574
auto rpcJ = app_.journal("RPC");
570575

571576
Json::Value jsonOrig;
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
#pragma once
2+
3+
/** Convenience macros for instrumenting code with OpenTelemetry trace spans.
4+
5+
When XRPL_ENABLE_TELEMETRY is defined, the macros create SpanGuard objects
6+
that manage span lifetime via RAII. When not defined, all macros expand to
7+
((void)0) with zero overhead.
8+
9+
Usage in instrumented code:
10+
@code
11+
XRPL_TRACE_RPC(app.getTelemetry(), "rpc.command." + name);
12+
XRPL_TRACE_SET_ATTR("xrpl.rpc.command", name);
13+
XRPL_TRACE_SET_ATTR("xrpl.rpc.status", "success");
14+
@endcode
15+
16+
@note Macro parameter names use leading/trailing underscores
17+
(e.g. _tel_obj_) to avoid colliding with identifiers in the macro body,
18+
specifically the ::xrpl::telemetry:: namespace qualifier.
19+
*/
20+
21+
#ifdef XRPL_ENABLE_TELEMETRY
22+
23+
#include <xrpl/telemetry/SpanGuard.h>
24+
#include <xrpl/telemetry/Telemetry.h>
25+
26+
#include <optional>
27+
28+
namespace xrpl {
29+
namespace telemetry {
30+
31+
/** Start an unconditional span, ended when the guard goes out of scope.
32+
@param _tel_obj_ Telemetry instance reference.
33+
@param _span_name_ Span name string.
34+
*/
35+
#define XRPL_TRACE_SPAN(_tel_obj_, _span_name_) \
36+
auto _xrpl_span_ = (_tel_obj_).startSpan(_span_name_); \
37+
::xrpl::telemetry::SpanGuard _xrpl_guard_(_xrpl_span_)
38+
39+
/** Start an unconditional span with a specific SpanKind.
40+
@param _tel_obj_ Telemetry instance reference.
41+
@param _span_name_ Span name string.
42+
@param _span_kind_ opentelemetry::trace::SpanKind value.
43+
*/
44+
#define XRPL_TRACE_SPAN_KIND(_tel_obj_, _span_name_, _span_kind_) \
45+
auto _xrpl_span_ = (_tel_obj_).startSpan(_span_name_, _span_kind_); \
46+
::xrpl::telemetry::SpanGuard _xrpl_guard_(_xrpl_span_)
47+
48+
/** Conditionally start a span for RPC tracing.
49+
The span is only created if shouldTraceRpc() returns true.
50+
@param _tel_obj_ Telemetry instance reference.
51+
@param _span_name_ Span name string.
52+
*/
53+
#define XRPL_TRACE_RPC(_tel_obj_, _span_name_) \
54+
std::optional<::xrpl::telemetry::SpanGuard> _xrpl_guard_; \
55+
if ((_tel_obj_).shouldTraceRpc()) \
56+
{ \
57+
_xrpl_guard_.emplace((_tel_obj_).startSpan(_span_name_)); \
58+
}
59+
60+
/** Conditionally start a span for transaction tracing.
61+
The span is only created if shouldTraceTransactions() returns true.
62+
@param _tel_obj_ Telemetry instance reference.
63+
@param _span_name_ Span name string.
64+
*/
65+
#define XRPL_TRACE_TX(_tel_obj_, _span_name_) \
66+
std::optional<::xrpl::telemetry::SpanGuard> _xrpl_guard_; \
67+
if ((_tel_obj_).shouldTraceTransactions()) \
68+
{ \
69+
_xrpl_guard_.emplace((_tel_obj_).startSpan(_span_name_)); \
70+
}
71+
72+
/** Conditionally start a span for consensus tracing.
73+
The span is only created if shouldTraceConsensus() returns true.
74+
@param _tel_obj_ Telemetry instance reference.
75+
@param _span_name_ Span name string.
76+
*/
77+
#define XRPL_TRACE_CONSENSUS(_tel_obj_, _span_name_) \
78+
std::optional<::xrpl::telemetry::SpanGuard> _xrpl_guard_; \
79+
if ((_tel_obj_).shouldTraceConsensus()) \
80+
{ \
81+
_xrpl_guard_.emplace((_tel_obj_).startSpan(_span_name_)); \
82+
}
83+
84+
/** Set a key-value attribute on the current span (if it exists).
85+
Must be used after one of the XRPL_TRACE_* span macros.
86+
*/
87+
#define XRPL_TRACE_SET_ATTR(key, value) \
88+
if (_xrpl_guard_.has_value()) \
89+
{ \
90+
_xrpl_guard_->setAttribute(key, value); \
91+
}
92+
93+
/** Record an exception on the current span and mark it as error.
94+
Must be used after one of the XRPL_TRACE_* span macros.
95+
*/
96+
#define XRPL_TRACE_EXCEPTION(e) \
97+
if (_xrpl_guard_.has_value()) \
98+
{ \
99+
_xrpl_guard_->recordException(e); \
100+
}
101+
102+
} // namespace telemetry
103+
} // namespace xrpl
104+
105+
#else // XRPL_ENABLE_TELEMETRY not defined
106+
107+
#define XRPL_TRACE_SPAN(_tel_obj_, _span_name_) ((void)0)
108+
#define XRPL_TRACE_SPAN_KIND(_tel_obj_, _span_name_, _span_kind_) ((void)0)
109+
#define XRPL_TRACE_RPC(_tel_obj_, _span_name_) ((void)0)
110+
#define XRPL_TRACE_TX(_tel_obj_, _span_name_) ((void)0)
111+
#define XRPL_TRACE_CONSENSUS(_tel_obj_, _span_name_) ((void)0)
112+
#define XRPL_TRACE_SET_ATTR(key, value) ((void)0)
113+
#define XRPL_TRACE_EXCEPTION(e) ((void)0)
114+
115+
#endif // XRPL_ENABLE_TELEMETRY

0 commit comments

Comments
 (0)