mirror of
https://github.com/XRPLF/rippled.git
synced 2026-08-03 04:01:01 +00:00
refactor(telemetry): extract span name constants into modular headers
Centralise scattered string literals into compile-time constants using StaticStr<N> and join() for dot-separated composition. Shared primitives live in SpanNames.h; RPC-specific names in RpcSpanNames.h. Future modules (consensus, peer, ledger) add their own *SpanNames.h without bloating the central header. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -44,21 +44,20 @@
|
||||
|
||||
1. Basic RPC tracing (factory method with category):
|
||||
@code
|
||||
// Define prefix at class level:
|
||||
static constexpr std::string_view spanPrefix_ = "rpc.command";
|
||||
#include <xrpld/rpc/detail/RpcSpanNames.h>
|
||||
|
||||
// At the call site:
|
||||
// At the call site (constants from RpcSpanNames.h):
|
||||
auto span = SpanGuard::span(
|
||||
TraceCategory::Rpc, spanPrefix_, "submit");
|
||||
span.setAttribute("xrpl.rpc.command", "submit");
|
||||
span.setAttribute("xrpl.rpc.status", "success");
|
||||
TraceCategory::Rpc, rpc_span::prefix::command, "submit");
|
||||
span.setAttribute(rpc_span::attr::command, "submit");
|
||||
span.setAttribute(rpc_span::attr::status, rpc_span::val::success);
|
||||
// span ended automatically on scope exit
|
||||
@endcode
|
||||
|
||||
2. Error recording:
|
||||
@code
|
||||
auto span = SpanGuard::span(
|
||||
TraceCategory::Rpc, "rpc.command", "submit");
|
||||
TraceCategory::Rpc, rpc_span::prefix::command, "submit");
|
||||
try {
|
||||
doWork();
|
||||
span.setOk();
|
||||
@@ -71,7 +70,7 @@
|
||||
@code
|
||||
// Thread A: create span and capture context
|
||||
auto span = SpanGuard::span(
|
||||
TraceCategory::Consensus, "consensus", "round");
|
||||
TraceCategory::Consensus, seg::consensus, "round");
|
||||
auto ctx = span.captureContext();
|
||||
|
||||
// Thread B: create child with captured context
|
||||
@@ -81,17 +80,17 @@
|
||||
4. Conditional check (rarely needed — methods are no-ops on null):
|
||||
@code
|
||||
auto span = SpanGuard::span(
|
||||
TraceCategory::Rpc, "rpc", "request");
|
||||
TraceCategory::Rpc, rpc_span::prefix::rpc, "request");
|
||||
if (span) {
|
||||
// expensive attribute computation only when active
|
||||
span.setAttribute("xrpl.rpc.payload_size", computeSize());
|
||||
span.setAttribute(rpc_span::attr::payloadSize, computeSize());
|
||||
}
|
||||
@endcode
|
||||
|
||||
5. Tail-based filtering via discard():
|
||||
@code
|
||||
auto span = SpanGuard::span(
|
||||
TraceCategory::Transactions, "tx", "process");
|
||||
TraceCategory::Transactions, seg::tx, "process");
|
||||
auto result = preflight(tx);
|
||||
if (result != tesSUCCESS) {
|
||||
span.discard(); // drop span, never exported
|
||||
|
||||
114
include/xrpl/telemetry/SpanNames.h
Normal file
114
include/xrpl/telemetry/SpanNames.h
Normal file
@@ -0,0 +1,114 @@
|
||||
#pragma once
|
||||
|
||||
/** 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
|
||||
* 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
|
||||
* concatenation support. boost::static_string is not constexpr.
|
||||
* StaticStr<N> exists specifically for compile-time dot-join composition.
|
||||
*
|
||||
* Naming conventions follow OpenTelemetry semantic conventions:
|
||||
* - Attribute keys: "xrpl.<subsystem>.<field>"
|
||||
* - Span prefixes: "<subsystem>[.<component>]"
|
||||
*/
|
||||
|
||||
#include <cstddef>
|
||||
#include <string_view>
|
||||
|
||||
namespace xrpl {
|
||||
namespace telemetry {
|
||||
|
||||
// ===== Compile-time string utility =========================================
|
||||
|
||||
/// 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
|
||||
{
|
||||
char data[N + 1]{};
|
||||
static constexpr std::size_t size = N;
|
||||
|
||||
constexpr StaticStr() = default;
|
||||
|
||||
constexpr explicit StaticStr(char const (&str)[N + 1])
|
||||
{
|
||||
for (std::size_t i = 0; i <= N; ++i)
|
||||
data[i] = str[i];
|
||||
}
|
||||
|
||||
constexpr
|
||||
operator std::string_view() const noexcept
|
||||
{
|
||||
return {data, N};
|
||||
}
|
||||
};
|
||||
|
||||
/// 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.
|
||||
template <std::size_t N>
|
||||
constexpr auto
|
||||
makeStr(char const (&str)[N])
|
||||
{
|
||||
return StaticStr<N - 1>(str);
|
||||
}
|
||||
|
||||
/// 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)
|
||||
{
|
||||
constexpr std::size_t len = A + 1 + B; // lhs + '.' + rhs
|
||||
StaticStr<len> result;
|
||||
std::size_t pos = 0;
|
||||
for (std::size_t i = 0; i < A; ++i)
|
||||
result.data[pos++] = lhs.data[i];
|
||||
result.data[pos++] = '.';
|
||||
for (std::size_t i = 0; i < B; ++i)
|
||||
result.data[pos++] = rhs.data[i];
|
||||
result.data[pos] = '\0';
|
||||
return result;
|
||||
}
|
||||
|
||||
// ===== Shared root segments ================================================
|
||||
|
||||
namespace seg {
|
||||
inline constexpr auto xrpl = makeStr("xrpl");
|
||||
inline constexpr auto rpc = makeStr("rpc");
|
||||
inline constexpr auto tx = makeStr("tx");
|
||||
inline constexpr auto consensus = makeStr("consensus");
|
||||
inline constexpr auto peer = makeStr("peer");
|
||||
inline constexpr auto ledger = makeStr("ledger");
|
||||
inline constexpr auto network = makeStr("network");
|
||||
inline constexpr auto link = makeStr("link");
|
||||
} // namespace seg
|
||||
|
||||
// ===== Shared attribute keys (used across modules) =========================
|
||||
|
||||
namespace attr {
|
||||
inline constexpr auto networkId = join(join(seg::xrpl, seg::network), makeStr("id"));
|
||||
inline constexpr auto networkType = join(join(seg::xrpl, seg::network), makeStr("type"));
|
||||
inline constexpr auto linkType = join(join(seg::xrpl, seg::link), makeStr("type"));
|
||||
} // namespace attr
|
||||
|
||||
// ===== Shared attribute values =============================================
|
||||
|
||||
namespace attr_val {
|
||||
inline constexpr auto success = makeStr("success");
|
||||
inline constexpr auto error = makeStr("error");
|
||||
inline constexpr auto followsFrom = makeStr("follows_from");
|
||||
} // namespace attr_val
|
||||
|
||||
} // namespace telemetry
|
||||
} // namespace xrpl
|
||||
@@ -20,9 +20,9 @@
|
||||
|
||||
#ifdef XRPL_ENABLE_TELEMETRY
|
||||
|
||||
#include <xrpl/telemetry/SpanGuard.h>
|
||||
|
||||
#include <xrpl/telemetry/DiscardFlag.h>
|
||||
#include <xrpl/telemetry/SpanGuard.h>
|
||||
#include <xrpl/telemetry/SpanNames.h>
|
||||
#include <xrpl/telemetry/Telemetry.h>
|
||||
|
||||
#include <opentelemetry/context/runtime_context.h>
|
||||
@@ -188,7 +188,10 @@ SpanGuard::linkedSpan(std::string_view name) const
|
||||
|
||||
return SpanGuard(
|
||||
std::make_unique<Impl>(tracer->StartSpan(
|
||||
std::string(name), {}, {{spanCtx, {{"xrpl.link.type", "follows_from"}}}}, opts)));
|
||||
std::string(name),
|
||||
{},
|
||||
{{spanCtx, {{std::string(attr::linkType), std::string(attr_val::followsFrom)}}}},
|
||||
opts)));
|
||||
}
|
||||
|
||||
SpanGuard
|
||||
@@ -218,7 +221,8 @@ SpanGuard::linkedSpan(std::string_view name, SpanContext const& linkCtx)
|
||||
std::make_unique<Impl>(tracer->StartSpan(
|
||||
std::string(name),
|
||||
{},
|
||||
{{linkSpan->GetContext(), {{"xrpl.link.type", "follows_from"}}}},
|
||||
{{linkSpan->GetContext(),
|
||||
{{std::string(attr::linkType), std::string(attr_val::followsFrom)}}}},
|
||||
opts)));
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/telemetry/DiscardFlag.h>
|
||||
#include <xrpl/telemetry/SpanNames.h>
|
||||
#include <xrpl/telemetry/Telemetry.h>
|
||||
|
||||
#include <opentelemetry/exporters/otlp/otlp_http_exporter_factory.h>
|
||||
@@ -279,8 +280,8 @@ public:
|
||||
{resource::SemanticConventions::kServiceName, setup_.serviceName},
|
||||
{resource::SemanticConventions::kServiceVersion, setup_.serviceVersion},
|
||||
{resource::SemanticConventions::kServiceInstanceId, setup_.serviceInstanceId},
|
||||
{"xrpl.network.id", static_cast<int64_t>(setup_.networkId)},
|
||||
{"xrpl.network.type", setup_.networkType},
|
||||
{std::string(attr::networkId), static_cast<int64_t>(setup_.networkId)},
|
||||
{std::string(attr::networkType), setup_.networkType},
|
||||
});
|
||||
|
||||
// Configure sampler
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <xrpld/rpc/Role.h>
|
||||
#include <xrpld/rpc/Status.h>
|
||||
#include <xrpld/rpc/detail/Handler.h>
|
||||
#include <xrpld/rpc/detail/RpcSpanNames.h>
|
||||
#include <xrpld/rpc/detail/Tuning.h>
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
@@ -162,10 +163,13 @@ template <class Object, class Method>
|
||||
Status
|
||||
callMethod(JsonContext& context, Method method, std::string const& name, Object& result)
|
||||
{
|
||||
auto span = SpanGuard::span(TraceCategory::Rpc, "rpc.command", name);
|
||||
span.setAttribute("xrpl.rpc.command", name.c_str());
|
||||
span.setAttribute("xrpl.rpc.version", static_cast<int64_t>(context.apiVersion));
|
||||
span.setAttribute("xrpl.rpc.role", (context.role == Role::ADMIN ? "admin" : "user"));
|
||||
auto span = SpanGuard::span(TraceCategory::Rpc, rpc_span::prefix::command, name);
|
||||
span.setAttribute(rpc_span::attr::command, name.c_str());
|
||||
span.setAttribute(rpc_span::attr::version, static_cast<int64_t>(context.apiVersion));
|
||||
span.setAttribute(
|
||||
rpc_span::attr::role,
|
||||
context.role == Role::ADMIN ? std::string_view(rpc_span::val::admin)
|
||||
: std::string_view(rpc_span::val::user));
|
||||
|
||||
static std::atomic<std::uint64_t> requestId{0};
|
||||
auto& perfLog = context.app.getPerfLog();
|
||||
@@ -182,7 +186,7 @@ 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);
|
||||
span.setAttribute("xrpl.rpc.status", "success");
|
||||
span.setAttribute(rpc_span::attr::status, rpc_span::val::success);
|
||||
return ret;
|
||||
}
|
||||
catch (std::exception& e)
|
||||
@@ -190,7 +194,7 @@ callMethod(JsonContext& context, Method method, std::string const& name, Object&
|
||||
perfLog.rpcError(name, curId);
|
||||
JLOG(context.j.info()) << "Caught throw: " << e.what();
|
||||
span.recordException(e);
|
||||
span.setAttribute("xrpl.rpc.status", "error");
|
||||
span.setAttribute(rpc_span::attr::status, rpc_span::val::error);
|
||||
|
||||
if (context.loadType == Resource::feeReferenceRPC)
|
||||
context.loadType = Resource::feeExceptionRPC;
|
||||
|
||||
72
src/xrpld/rpc/detail/RpcSpanNames.h
Normal file
72
src/xrpld/rpc/detail/RpcSpanNames.h
Normal file
@@ -0,0 +1,72 @@
|
||||
#pragma once
|
||||
|
||||
/** 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()
|
||||
* primitives from <xrpl/telemetry/SpanNames.h>.
|
||||
*
|
||||
* Usage:
|
||||
* @code
|
||||
* #include <xrpld/rpc/detail/RpcSpanNames.h>
|
||||
* using namespace telemetry;
|
||||
*
|
||||
* auto span = SpanGuard::span(
|
||||
* TraceCategory::Rpc, rpc_span::prefix::command, "submit");
|
||||
* span.setAttribute(rpc_span::attr::command, "submit");
|
||||
* span.setAttribute(rpc_span::attr::status, rpc_span::val::success);
|
||||
* @endcode
|
||||
*/
|
||||
|
||||
#include <xrpl/telemetry/SpanNames.h>
|
||||
|
||||
namespace xrpl {
|
||||
namespace telemetry {
|
||||
namespace rpc_span {
|
||||
|
||||
// ===== Span prefixes =======================================================
|
||||
|
||||
namespace prefix {
|
||||
/// "rpc" — root prefix for transport-level spans.
|
||||
inline constexpr auto rpc = seg::rpc;
|
||||
/// "rpc.command" — prefix for individual RPC command spans.
|
||||
inline constexpr auto command = join(seg::rpc, makeStr("command"));
|
||||
} // namespace prefix
|
||||
|
||||
// ===== Span operation suffixes =============================================
|
||||
|
||||
namespace op {
|
||||
inline constexpr auto wsMessage = makeStr("ws_message");
|
||||
inline constexpr auto httpRequest = makeStr("http_request");
|
||||
inline constexpr auto process = makeStr("process");
|
||||
} // namespace op
|
||||
|
||||
// ===== Attribute keys ======================================================
|
||||
|
||||
namespace attr {
|
||||
inline constexpr auto xrplRpc = join(seg::xrpl, seg::rpc);
|
||||
|
||||
/// "xrpl.rpc.command"
|
||||
inline constexpr auto command = join(xrplRpc, makeStr("command"));
|
||||
/// "xrpl.rpc.version"
|
||||
inline constexpr auto version = join(xrplRpc, makeStr("version"));
|
||||
/// "xrpl.rpc.role"
|
||||
inline constexpr auto role = join(xrplRpc, makeStr("role"));
|
||||
/// "xrpl.rpc.status"
|
||||
inline constexpr auto status = join(xrplRpc, makeStr("status"));
|
||||
/// "xrpl.rpc.payload_size"
|
||||
inline constexpr auto payloadSize = join(xrplRpc, makeStr("payload_size"));
|
||||
} // namespace attr
|
||||
|
||||
// ===== Attribute values ====================================================
|
||||
|
||||
namespace val {
|
||||
using telemetry::attr_val::error;
|
||||
using telemetry::attr_val::success;
|
||||
inline constexpr auto admin = makeStr("admin");
|
||||
inline constexpr auto user = makeStr("user");
|
||||
} // namespace val
|
||||
|
||||
} // namespace rpc_span
|
||||
} // namespace telemetry
|
||||
} // namespace xrpl
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <xrpld/overlay/Overlay.h>
|
||||
#include <xrpld/rpc/RPCHandler.h>
|
||||
#include <xrpld/rpc/Role.h>
|
||||
#include <xrpld/rpc/detail/RpcSpanNames.h>
|
||||
#include <xrpld/rpc/detail/Tuning.h>
|
||||
#include <xrpld/rpc/detail/WSInfoSub.h>
|
||||
#include <xrpld/rpc/json_body.h>
|
||||
@@ -419,7 +420,7 @@ ServerHandler::processSession(
|
||||
std::shared_ptr<JobQueue::Coro> const& coro,
|
||||
Json::Value const& jv)
|
||||
{
|
||||
auto span = SpanGuard::span(TraceCategory::Rpc, "rpc", "ws_message");
|
||||
auto span = SpanGuard::span(TraceCategory::Rpc, rpc_span::prefix::rpc, rpc_span::op::wsMessage);
|
||||
auto is = std::static_pointer_cast<WSInfoSub>(session->appDefined);
|
||||
if (is->getConsumer().disconnect(m_journal))
|
||||
{
|
||||
@@ -504,7 +505,7 @@ ServerHandler::processSession(
|
||||
JLOG(m_journal.error()) << "Exception while processing WS: " << ex.what() << "\n"
|
||||
<< "Input JSON: " << Json::Compact{Json::Value{jv}};
|
||||
span.recordException(ex);
|
||||
span.setAttribute("xrpl.rpc.status", "error");
|
||||
span.setAttribute(rpc_span::attr::status, rpc_span::val::error);
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
@@ -564,7 +565,8 @@ ServerHandler::processSession(
|
||||
std::shared_ptr<Session> const& session,
|
||||
std::shared_ptr<JobQueue::Coro> coro)
|
||||
{
|
||||
auto span = SpanGuard::span(TraceCategory::Rpc, "rpc", "http_request");
|
||||
auto span =
|
||||
SpanGuard::span(TraceCategory::Rpc, rpc_span::prefix::rpc, rpc_span::op::httpRequest);
|
||||
|
||||
processRequest(
|
||||
session->port(),
|
||||
@@ -616,7 +618,7 @@ ServerHandler::processRequest(
|
||||
std::string_view forwardedFor,
|
||||
std::string_view user)
|
||||
{
|
||||
auto span = SpanGuard::span(TraceCategory::Rpc, "rpc", "process");
|
||||
auto span = SpanGuard::span(TraceCategory::Rpc, rpc_span::prefix::rpc, rpc_span::op::process);
|
||||
auto rpcJ = app_.getJournal("RPC");
|
||||
|
||||
Json::Value jsonOrig;
|
||||
@@ -894,7 +896,7 @@ ServerHandler::processRequest(
|
||||
<< "Internal error : " << ex.what()
|
||||
<< " when processing request: " << Json::Compact{Json::Value{params}};
|
||||
span.recordException(ex);
|
||||
span.setAttribute("xrpl.rpc.status", "error");
|
||||
span.setAttribute(rpc_span::attr::status, rpc_span::val::error);
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user