diff --git a/CMakeLists.txt b/CMakeLists.txt index 3fa406e61e..26189b4981 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -119,7 +119,7 @@ endif() # OpenTelemetry distributed tracing (optional). # When ON, links against opentelemetry-cpp and defines XRPL_ENABLE_TELEMETRY -# so that tracing macros in TracingInstrumentation.h are compiled in. +# so that SpanGuard factory methods produce real OTel spans. # When OFF (default), all tracing code compiles to no-ops with zero overhead. # Enable via: conan install -o telemetry=True, or cmake -Dtelemetry=ON. option(telemetry "Enable OpenTelemetry tracing" OFF) diff --git a/src/xrpld/rpc/detail/RPCHandler.cpp b/src/xrpld/rpc/detail/RPCHandler.cpp index 7f99096032..a9ab20713b 100644 --- a/src/xrpld/rpc/detail/RPCHandler.cpp +++ b/src/xrpld/rpc/detail/RPCHandler.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include #include @@ -17,6 +16,9 @@ #include #include #include +#include +#include +#include #include #include @@ -158,10 +160,10 @@ template Status callMethod(JsonContext& context, Method method, std::string const& name, Object& result) { - XRPL_TRACE_RPC(context.app.getTelemetry(), "rpc.command." + name); - XRPL_TRACE_SET_ATTR("xrpl.rpc.command", name.c_str()); - XRPL_TRACE_SET_ATTR("xrpl.rpc.version", static_cast(context.apiVersion)); - XRPL_TRACE_SET_ATTR("xrpl.rpc.role", (context.role == Role::ADMIN ? "admin" : "user")); + auto span = telemetry::SpanGuard::rpcSpan("rpc.command." + name); + span.setAttribute("xrpl.rpc.command", name.c_str()); + span.setAttribute("xrpl.rpc.version", static_cast(context.apiVersion)); + span.setAttribute("xrpl.rpc.role", (context.role == Role::ADMIN ? "admin" : "user")); static std::atomic requestId{0}; auto& perfLog = context.app.getPerfLog(); @@ -178,15 +180,15 @@ callMethod(JsonContext& context, Method method, std::string const& name, Object& JLOG(context.j.debug()) << "RPC call " << name << " completed in " << ((end - start).count() / 1000000000.0) << "seconds"; perfLog.rpcFinish(name, curId); - XRPL_TRACE_SET_ATTR("xrpl.rpc.status", "success"); + span.setAttribute("xrpl.rpc.status", "success"); return ret; } catch (std::exception& e) { perfLog.rpcError(name, curId); JLOG(context.j.info()) << "Caught throw: " << e.what(); - XRPL_TRACE_EXCEPTION(e); - XRPL_TRACE_SET_ATTR("xrpl.rpc.status", "error"); + span.recordException(e); + span.setAttribute("xrpl.rpc.status", "error"); if (context.loadType == Resource::feeReferenceRPC) context.loadType = Resource::feeExceptionRPC; diff --git a/src/xrpld/rpc/detail/ServerHandler.cpp b/src/xrpld/rpc/detail/ServerHandler.cpp index f3938fa38e..aa818dc6e4 100644 --- a/src/xrpld/rpc/detail/ServerHandler.cpp +++ b/src/xrpld/rpc/detail/ServerHandler.cpp @@ -8,7 +8,6 @@ #include #include #include -#include #include #include @@ -45,6 +44,7 @@ #include #include #include +#include #include #include @@ -418,7 +418,7 @@ ServerHandler::processSession( std::shared_ptr const& coro, Json::Value const& jv) { - XRPL_TRACE_RPC(app_.getTelemetry(), "rpc.ws_message"); + auto span = telemetry::SpanGuard::rpcSpan("rpc.ws_message"); auto is = std::static_pointer_cast(session->appDefined); if (is->getConsumer().disconnect(m_journal)) { @@ -502,8 +502,8 @@ ServerHandler::processSession( jr[jss::result] = RPC::make_error(rpcINTERNAL); JLOG(m_journal.error()) << "Exception while processing WS: " << ex.what() << "\n" << "Input JSON: " << Json::Compact{Json::Value{jv}}; - XRPL_TRACE_EXCEPTION(ex); - XRPL_TRACE_SET_ATTR("xrpl.rpc.status", "error"); + span.recordException(ex); + span.setAttribute("xrpl.rpc.status", "error"); // LCOV_EXCL_STOP } @@ -563,7 +563,7 @@ ServerHandler::processSession( std::shared_ptr const& session, std::shared_ptr coro) { - XRPL_TRACE_RPC(app_.getTelemetry(), "rpc.http_request"); + auto span = telemetry::SpanGuard::rpcSpan("rpc.http_request"); processRequest( session->port(), @@ -615,7 +615,7 @@ ServerHandler::processRequest( std::string_view forwardedFor, std::string_view user) { - XRPL_TRACE_RPC(app_.getTelemetry(), "rpc.process"); + auto span = telemetry::SpanGuard::rpcSpan("rpc.process"); auto rpcJ = app_.getJournal("RPC"); Json::Value jsonOrig; @@ -892,8 +892,8 @@ ServerHandler::processRequest( JLOG(m_journal.error()) << "Internal error : " << ex.what() << " when processing request: " << Json::Compact{Json::Value{params}}; - XRPL_TRACE_EXCEPTION(ex); - XRPL_TRACE_SET_ATTR("xrpl.rpc.status", "error"); + span.recordException(ex); + span.setAttribute("xrpl.rpc.status", "error"); // LCOV_EXCL_STOP } diff --git a/src/xrpld/telemetry/TracingInstrumentation.h b/src/xrpld/telemetry/TracingInstrumentation.h deleted file mode 100644 index 2f82bf3588..0000000000 --- a/src/xrpld/telemetry/TracingInstrumentation.h +++ /dev/null @@ -1,147 +0,0 @@ -#pragma once - -/** Convenience macros for instrumenting code with OpenTelemetry trace spans. - - When XRPL_ENABLE_TELEMETRY is defined, the macros create SpanGuard objects - that manage span lifetime via RAII. When not defined, all macros expand to - ((void)0) with zero overhead. - - All span-creation macros produce a std::optional named - _xrpl_guard_. The accessor macros (XRPL_TRACE_SET_ATTR, - XRPL_TRACE_EXCEPTION) reference this variable by name, so they must - appear in the same scope after exactly one span-creation macro. - - @note Only one XRPL_TRACE_* span-creation macro may appear per scope, - because they all declare a variable named _xrpl_guard_. Nested spans - across function boundaries are fine (each function has its own scope). - - @note These macros must not be used in single-statement if/else without - braces. The span-creation macros expand to multiple statements that - declare variables needed by the accessor macros. - - @note Thread safety: Each SpanGuard binds to the constructing thread's - OTel context stack via Scope. Do not move a guard across threads. - - Usage examples: - - 1. Basic RPC tracing: - @code - XRPL_TRACE_RPC(app.getTelemetry(), "rpc.command." + name); - XRPL_TRACE_SET_ATTR("xrpl.rpc.command", name); - XRPL_TRACE_SET_ATTR("xrpl.rpc.status", "success"); - @endcode - - 2. Exception recording: - @code - XRPL_TRACE_RPC(telemetry, "rpc.process"); - try { - doWork(); - } catch (std::exception const& e) { - XRPL_TRACE_EXCEPTION(e); - XRPL_TRACE_SET_ATTR("xrpl.rpc.status", "error"); - } - @endcode - - 3. Unconditional span: - @code - XRPL_TRACE_SPAN(telemetry, "tx.apply"); - XRPL_TRACE_SET_ATTR("xrpl.tx.hash", txHash); - @endcode -*/ - -#ifdef XRPL_ENABLE_TELEMETRY - -#include -#include - -#include - -/** Start an unconditional span, ended when the guard goes out of scope. - @param _tel_obj_ Telemetry instance reference. - @param _span_name_ Span name string. -*/ -#define XRPL_TRACE_SPAN(_tel_obj_, _span_name_) \ - std::optional<::xrpl::telemetry::SpanGuard> _xrpl_guard_( \ - std::in_place, (_tel_obj_).startSpan(_span_name_)) - -/** Start an unconditional span with a specific SpanKind. - @param _tel_obj_ Telemetry instance reference. - @param _span_name_ Span name string. - @param _span_kind_ opentelemetry::trace::SpanKind value. -*/ -#define XRPL_TRACE_SPAN_KIND(_tel_obj_, _span_name_, _span_kind_) \ - std::optional<::xrpl::telemetry::SpanGuard> _xrpl_guard_( \ - std::in_place, (_tel_obj_).startSpan(_span_name_, _span_kind_)) - -/** Conditionally start a span for RPC tracing. - The span is only created if shouldTraceRpc() returns true. - @param _tel_obj_ Telemetry instance reference. - @param _span_name_ Span name string. -*/ -#define XRPL_TRACE_RPC(_tel_obj_, _span_name_) \ - auto& _xrpl_tel_ = (_tel_obj_); \ - std::optional<::xrpl::telemetry::SpanGuard> _xrpl_guard_; \ - if (_xrpl_tel_.shouldTraceRpc()) \ - { \ - _xrpl_guard_.emplace(_xrpl_tel_.startSpan(_span_name_)); \ - } - -/** Conditionally start a span for transaction tracing. - The span is only created if shouldTraceTransactions() returns true. - @param _tel_obj_ Telemetry instance reference. - @param _span_name_ Span name string. -*/ -#define XRPL_TRACE_TX(_tel_obj_, _span_name_) \ - auto& _xrpl_tel_ = (_tel_obj_); \ - std::optional<::xrpl::telemetry::SpanGuard> _xrpl_guard_; \ - if (_xrpl_tel_.shouldTraceTransactions()) \ - { \ - _xrpl_guard_.emplace(_xrpl_tel_.startSpan(_span_name_)); \ - } - -/** Conditionally start a span for consensus tracing. - The span is only created if shouldTraceConsensus() returns true. - @param _tel_obj_ Telemetry instance reference. - @param _span_name_ Span name string. -*/ -#define XRPL_TRACE_CONSENSUS(_tel_obj_, _span_name_) \ - auto& _xrpl_tel_ = (_tel_obj_); \ - std::optional<::xrpl::telemetry::SpanGuard> _xrpl_guard_; \ - if (_xrpl_tel_.shouldTraceConsensus()) \ - { \ - _xrpl_guard_.emplace(_xrpl_tel_.startSpan(_span_name_)); \ - } - -/** Set a key-value attribute on the current span (if it exists). - Must be used after one of the XRPL_TRACE_* span-creation macros - in the same scope. -*/ -#define XRPL_TRACE_SET_ATTR(key, value) \ - do \ - { \ - if (_xrpl_guard_.has_value()) \ - _xrpl_guard_->setAttribute(key, value); \ - } while (0) - -/** Record an exception on the current span and mark it as error. - Must be used after one of the XRPL_TRACE_* span-creation macros - in the same scope. -*/ -#define XRPL_TRACE_EXCEPTION(e) \ - do \ - { \ - if (_xrpl_guard_.has_value()) \ - _xrpl_guard_->recordException(e); \ - } while (0) - -#else // XRPL_ENABLE_TELEMETRY not defined - -#define XRPL_TRACE_SPAN(_tel_obj_, _span_name_) ((void)0) -#define XRPL_TRACE_SPAN_KIND(_tel_obj_, _span_name_, _span_kind_) ((void)0) -#define XRPL_TRACE_RPC(_tel_obj_, _span_name_) ((void)0) -#define XRPL_TRACE_TX(_tel_obj_, _span_name_) ((void)0) -#define XRPL_TRACE_CONSENSUS(_tel_obj_, _span_name_) ((void)0) -#define XRPL_TRACE_SET_ATTR(key, value) ((void)0) -#define XRPL_TRACE_EXCEPTION(e) ((void)0) - -#endif // XRPL_ENABLE_TELEMETRY