diff --git a/src/xrpld/rpc/detail/ServerHandler.cpp b/src/xrpld/rpc/detail/ServerHandler.cpp index 705cee8853..f3938fa38e 100644 --- a/src/xrpld/rpc/detail/ServerHandler.cpp +++ b/src/xrpld/rpc/detail/ServerHandler.cpp @@ -303,8 +303,6 @@ buffers_to_string(ConstBufferSequence const& bs) void ServerHandler::onRequest(Session& session) { - XRPL_TRACE_RPC(app_.getTelemetry(), "rpc.request"); - // Make sure RPC is enabled on the port if (session.port().protocol.count("http") == 0 && session.port().protocol.count("https") == 0) { @@ -504,6 +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"); // LCOV_EXCL_STOP } @@ -563,6 +563,8 @@ ServerHandler::processSession( std::shared_ptr const& session, std::shared_ptr coro) { + XRPL_TRACE_RPC(app_.getTelemetry(), "rpc.http_request"); + processRequest( session->port(), buffers_to_string(session->request().body().data()), @@ -890,6 +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"); // LCOV_EXCL_STOP } diff --git a/src/xrpld/telemetry/TracingInstrumentation.h b/src/xrpld/telemetry/TracingInstrumentation.h index d7d2ecf912..2f82bf3588 100644 --- a/src/xrpld/telemetry/TracingInstrumentation.h +++ b/src/xrpld/telemetry/TracingInstrumentation.h @@ -6,16 +6,47 @@ that manage span lifetime via RAII. When not defined, all macros expand to ((void)0) with zero overhead. - Usage in instrumented code: + 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 - @note Macro parameter names use leading/trailing underscores - (e.g. _tel_obj_) to avoid colliding with identifiers in the macro body, - specifically the ::xrpl::telemetry:: namespace qualifier. + 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 @@ -25,36 +56,34 @@ #include -namespace xrpl { -namespace telemetry { - /** 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_) \ - auto _xrpl_span_ = (_tel_obj_).startSpan(_span_name_); \ - ::xrpl::telemetry::SpanGuard _xrpl_guard_(_xrpl_span_) +#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_) \ - auto _xrpl_span_ = (_tel_obj_).startSpan(_span_name_, _span_kind_); \ - ::xrpl::telemetry::SpanGuard _xrpl_guard_(_xrpl_span_) +#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_) \ - std::optional<::xrpl::telemetry::SpanGuard> _xrpl_guard_; \ - if ((_tel_obj_).shouldTraceRpc()) \ - { \ - _xrpl_guard_.emplace((_tel_obj_).startSpan(_span_name_)); \ +#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. @@ -62,11 +91,12 @@ namespace telemetry { @param _tel_obj_ Telemetry instance reference. @param _span_name_ Span name string. */ -#define XRPL_TRACE_TX(_tel_obj_, _span_name_) \ - std::optional<::xrpl::telemetry::SpanGuard> _xrpl_guard_; \ - if ((_tel_obj_).shouldTraceTransactions()) \ - { \ - _xrpl_guard_.emplace((_tel_obj_).startSpan(_span_name_)); \ +#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. @@ -74,33 +104,35 @@ namespace telemetry { @param _tel_obj_ Telemetry instance reference. @param _span_name_ Span name string. */ -#define XRPL_TRACE_CONSENSUS(_tel_obj_, _span_name_) \ - std::optional<::xrpl::telemetry::SpanGuard> _xrpl_guard_; \ - if ((_tel_obj_).shouldTraceConsensus()) \ - { \ - _xrpl_guard_.emplace((_tel_obj_).startSpan(_span_name_)); \ +#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 macros. + Must be used after one of the XRPL_TRACE_* span-creation macros + in the same scope. */ -#define XRPL_TRACE_SET_ATTR(key, value) \ - if (_xrpl_guard_.has_value()) \ - { \ - _xrpl_guard_->setAttribute(key, value); \ - } +#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 macros. + Must be used after one of the XRPL_TRACE_* span-creation macros + in the same scope. */ -#define XRPL_TRACE_EXCEPTION(e) \ - if (_xrpl_guard_.has_value()) \ - { \ - _xrpl_guard_->recordException(e); \ - } - -} // namespace telemetry -} // namespace xrpl +#define XRPL_TRACE_EXCEPTION(e) \ + do \ + { \ + if (_xrpl_guard_.has_value()) \ + _xrpl_guard_->recordException(e); \ + } while (0) #else // XRPL_ENABLE_TELEMETRY not defined