fix(telemetry): use underscore attr names and SpanNames constants in Phase 4

- tempo.yaml: align consensus filter tags with emitted keys
  (consensus_mode, consensus_round, ledger_seq) instead of dotted form
- haveConsensus(): set span attributes before early-return paths so the
  consensus.check span carries diagnostics even when consensus is not reached
- replace hardcoded consensus phase/result/vote literals with
  ConsensusSpanNames.h val constants; add val::phaseOpen/Establish/Accepted
- ConsensusReceiveTracing.h: use canonical consensus::span constants instead
  of duplicate inline detail:: names
- SpanGuardFactory test: use rpc_span / consensus::span constants now that
  levelization permits the dependency

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Pratik Mankawde
2026-06-10 17:07:10 +01:00
parent 4a0994209e
commit ffc197b914
10 changed files with 93 additions and 73 deletions

View File

@@ -4,6 +4,9 @@ Loop: test.jtx test.toplevel
Loop: test.jtx test.unit_test
test.unit_test ~= test.jtx
Loop: xrpl.telemetry xrpld.consensus
xrpld.consensus ~= xrpl.telemetry
Loop: xrpl.telemetry xrpld.rpc
xrpld.rpc > xrpl.telemetry

View File

@@ -207,6 +207,8 @@ test.unit_test > xrpl.protocol
tests.libxrpl > xrpl.basics
tests.libxrpl > xrpl.config
tests.libxrpl > xrpl.core
tests.libxrpl > xrpld.consensus
tests.libxrpl > xrpld.rpc
tests.libxrpl > xrpl.json
tests.libxrpl > xrpl.ledger
tests.libxrpl > xrpl.net
@@ -253,7 +255,6 @@ xrpl.shamap > xrpl.basics
xrpl.shamap > xrpl.nodestore
xrpl.shamap > xrpl.protocol
xrpl.telemetry > xrpl.config
xrpl.telemetry > xrpld.consensus
xrpl.tx > xrpl.basics
xrpl.tx > xrpl.core
xrpl.tx > xrpl.ledger
@@ -280,7 +281,6 @@ xrpld.consensus > xrpl.basics
xrpld.consensus > xrpl.json
xrpld.consensus > xrpl.ledger
xrpld.consensus > xrpl.protocol
xrpld.consensus > xrpl.telemetry
xrpld.core > xrpl.basics
xrpld.core > xrpl.config
xrpld.core > xrpl.core
@@ -332,4 +332,5 @@ xrpld.shamap > xrpld.core
xrpld.shamap > xrpl.protocol
xrpld.shamap > xrpl.shamap
xrpld.telemetry > xrpl.basics
xrpld.telemetry > xrpld.consensus
xrpld.telemetry > xrpl.telemetry

View File

@@ -16,7 +16,7 @@
"nlohmann_json/3.11.3#45828be26eb619a2e04ca517bb7b828d%1701220705.259",
"lz4/1.10.0#59fc63cac7f10fbe8e05c7e62c2f3504%1765850143.914",
"libiconv/1.17#1e65319e945f2d31941a9d28cc13c058%1765842973.492",
"libcurl/8.20.0#465ac276192c197ddc6a9f4494004278%1779353234.048",
"libcurl/8.20.0#c90b0c91a33d9a79b519c1c70bafc823%1780907438.587",
"libbacktrace/cci.20210118#a7691bfccd8caaf66309df196790a5a1%1765842973.03",
"libarchive/3.8.7#c446109bd1f1d8ba7936c94189bc50e6%1778091117.848",
"jemalloc/5.3.1#1fc58d55316041f10fbc1e8a2eae632a%1776700028.228",

View File

@@ -126,17 +126,17 @@ datasources:
type: dynamic
# Phase 4: Consensus tracing filters
- id: consensus-mode
tag: xrpl.consensus.mode
tag: consensus_mode
operator: "="
scope: span
type: static
- id: consensus-round
tag: xrpl.consensus.round
tag: consensus_round
operator: "="
scope: span
type: dynamic
- id: consensus-ledger-seq
tag: xrpl.ledger.seq
tag: ledger_seq
operator: "="
scope: span
type: static

View File

@@ -1,12 +1,13 @@
#include <xrpld/consensus/ConsensusSpanNames.h>
#include <xrpld/rpc/detail/RpcSpanNames.h>
#include <xrpl/telemetry/SpanGuard.h>
#include <xrpl/telemetry/SpanNames.h>
#include <gtest/gtest.h>
#include <cstdint>
#include <exception>
#include <stdexcept>
#include <string>
#include <utility>
using namespace xrpl;
@@ -30,8 +31,8 @@ TEST(SpanGuardFactory, category_span_returns_null_when_disabled)
auto span = SpanGuard::span(TraceCategory::Rpc, "rpc", "test");
EXPECT_FALSE(span);
span.setAttribute("xrpl.rpc.command", "test");
span.setAttribute("xrpl.rpc.status", "success");
span.setAttribute(rpc_span::attr::command, "test");
span.setAttribute(rpc_span::attr::rpcStatus, rpc_span::val::success);
}
TEST(SpanGuardFactory, child_span_null_when_no_parent)
@@ -87,21 +88,22 @@ TEST(SpanGuardFactory, consensus_close_time_attributes)
{
// Verify the consensus attribute pattern compiles and
// doesn't crash with null SpanGuard.
namespace cs = consensus::span;
{
auto span = telemetry::SpanGuard::span(
telemetry::TraceCategory::Consensus, telemetry::seg::consensus, "accept.apply");
span.setAttribute("xrpl.consensus.ledger.seq", static_cast<int64_t>(42));
span.setAttribute("xrpl.consensus.close_time", static_cast<int64_t>(780000000));
span.setAttribute("xrpl.consensus.close_time_correct", true);
span.setAttribute("xrpl.consensus.close_resolution_ms", static_cast<int64_t>(30000));
span.setAttribute("xrpl.consensus.state", std::string("finished"));
span.setAttribute("xrpl.consensus.proposing", true);
span.setAttribute("xrpl.consensus.round_time_ms", static_cast<int64_t>(3500));
telemetry::TraceCategory::Consensus, telemetry::seg::consensus, cs::op::acceptApply);
span.setAttribute(cs::attr::ledgerSeq, static_cast<int64_t>(42));
span.setAttribute(cs::attr::closeTime, static_cast<int64_t>(780000000));
span.setAttribute(cs::attr::closeTimeCorrect, true);
span.setAttribute(cs::attr::closeResolutionMs, static_cast<int64_t>(30000));
span.setAttribute(cs::attr::consensusState, cs::val::finished);
span.setAttribute(cs::attr::proposing, true);
span.setAttribute(cs::attr::roundTimeMs, static_cast<int64_t>(3500));
}
{
auto span = telemetry::SpanGuard::span(
telemetry::TraceCategory::Consensus, telemetry::seg::consensus, "accept.apply");
span.setAttribute("xrpl.consensus.close_time_correct", false);
span.setAttribute("xrpl.consensus.state", std::string("moved_on"));
telemetry::TraceCategory::Consensus, telemetry::seg::consensus, cs::op::acceptApply);
span.setAttribute(cs::attr::closeTimeCorrect, false);
span.setAttribute(cs::attr::consensusState, cs::val::movedOn);
}
}

View File

@@ -1,5 +1,5 @@
#include <xrpl/basics/BasicConfig.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/config/BasicConfig.h>
#include <xrpl/telemetry/Telemetry.h>
#include <gtest/gtest.h>

View File

@@ -588,7 +588,8 @@ RCLConsensus::Adaptor::doAccept(
static_cast<int64_t>(
std::chrono::duration_cast<std::chrono::milliseconds>(closeResolution).count()));
doAcceptSpan.setAttribute(
cs::attr::consensusState, std::string(consensusFail ? "moved_on" : "finished"));
cs::attr::consensusState,
consensusFail ? std::string_view{cs::val::movedOn} : std::string_view{cs::val::finished});
doAcceptSpan.setAttribute(cs::attr::proposing, proposing);
doAcceptSpan.setAttribute(
cs::attr::roundTimeMs, static_cast<int64_t>(result.roundTime.read().count()));
@@ -1284,7 +1285,7 @@ RCLConsensus::Adaptor::startRoundTracing(RCLCxLedger const& prevLgr)
roundSpan_->setAttribute(cs::attr::previousProposers, static_cast<int64_t>(prevProposers_));
roundSpan_->setAttribute(
cs::attr::previousRoundTimeMs, static_cast<int64_t>(prevRoundTime_.load().count()));
roundSpan_->setAttribute(cs::attr::consensusPhase, "open");
roundSpan_->setAttribute(cs::attr::consensusPhase, cs::val::phaseOpen);
roundSpan_->addEvent(cs::event::phaseOpen);

View File

@@ -744,7 +744,9 @@ Consensus<Adaptor>::startRoundInternal(
// after the new round span is in place.
if (reason == StartRoundReason::Recovered)
{
adaptor_.onPhaseEvent(telemetry::consensus::span::event::phaseOpen, "open");
adaptor_.onPhaseEvent(
telemetry::consensus::span::event::phaseOpen,
telemetry::consensus::span::val::phaseOpen);
}
mode_.set(mode, adaptor_);
now_ = now;
@@ -994,7 +996,9 @@ Consensus<Adaptor>::simulate(
result_->proposers = prevProposers_ = currPeerPositions_.size();
prevRoundTime_ = result_->roundTime.read();
phase_ = ConsensusPhase::Accepted;
adaptor_.onPhaseEvent(telemetry::consensus::span::event::phaseAccepted, "accepted");
adaptor_.onPhaseEvent(
telemetry::consensus::span::event::phaseAccepted,
telemetry::consensus::span::val::phaseAccepted);
adaptor_.onForceAccept(
*result_, previousLedger_, closeResolution_, rawCloseTimes_, mode_.get(), getJson(true));
// NOLINTEND(bugprone-unchecked-optional-access)
@@ -1474,7 +1478,9 @@ Consensus<Adaptor>::phaseEstablish(std::unique_ptr<std::stringstream> const& clo
}
}
phase_ = ConsensusPhase::Accepted;
adaptor_.onPhaseEvent(telemetry::consensus::span::event::phaseAccepted, "accepted");
adaptor_.onPhaseEvent(
telemetry::consensus::span::event::phaseAccepted,
telemetry::consensus::span::val::phaseAccepted);
JLOG(j_.debug()) << "transitioned to ConsensusPhase::Accepted";
adaptor_.onAccept(
*result_,
@@ -1508,7 +1514,9 @@ Consensus<Adaptor>::closeLedger(std::unique_ptr<std::stringstream> const& clog)
}
openSpan_.reset();
phase_ = ConsensusPhase::Establish;
adaptor_.onPhaseEvent(telemetry::consensus::span::event::phaseEstablish, "establish");
adaptor_.onPhaseEvent(
telemetry::consensus::span::event::phaseEstablish,
telemetry::consensus::span::val::phaseEstablish);
JLOG(j_.debug()) << "transitioned to ConsensusPhase::Establish";
rawCloseTimes_.self = now_;
peerUnchangedCounter_ = 0;
@@ -1638,7 +1646,9 @@ Consensus<Adaptor>::updateOurPositions(std::unique_ptr<std::stringstream> const&
span.addEvent(
consensus::span::event::disputeResolve,
{{consensus::span::attr::txId, to_string(txId)},
{consensus::span::attr::disputeOurVote, dispute.getOurVote() ? "yes" : "no"},
{consensus::span::attr::disputeOurVote,
dispute.getOurVote() ? std::string_view{consensus::span::val::yes}
: std::string_view{consensus::span::val::no}},
{consensus::span::attr::disputeYays, yaysStr},
{consensus::span::attr::disputeNays, naysStr}});
}
@@ -1831,6 +1841,38 @@ Consensus<Adaptor>::haveConsensus(std::unique_ptr<std::stringstream> const& clog
j_,
clog);
// Set span attributes before the early-return branches below so the
// consensus.check span carries diagnostic data even when consensus is
// not reached (the No / Expired paths return early).
span.setAttribute(consensus::span::attr::agreeCount, static_cast<int64_t>(agree));
span.setAttribute(consensus::span::attr::disagreeCount, static_cast<int64_t>(disagree));
span.setAttribute(
consensus::span::attr::convergePercent, static_cast<int64_t>(convergePercent_));
span.setAttribute(consensus::span::attr::haveCloseTimeConsensus, haveCloseTimeConsensus_);
span.setAttribute(
consensus::span::attr::thresholdPercent,
static_cast<int64_t>(adaptor_.parms().avCtConsensusPct));
span.setAttribute(
consensus::span::attr::proposersFinished, static_cast<int64_t>(currentFinished));
span.setAttribute(consensus::span::attr::consensusStalled, stalled);
span.setAttribute(
consensus::span::attr::establishCounter, static_cast<int64_t>(establishCounter_));
std::string_view stateStr = consensus::span::val::no;
if (result_->state == ConsensusState::Yes)
{
stateStr = consensus::span::val::yes;
}
else if (result_->state == ConsensusState::MovedOn)
{
stateStr = consensus::span::val::movedOn;
}
else if (result_->state == ConsensusState::Expired)
{
stateStr = consensus::span::val::expired;
}
span.setAttribute(consensus::span::attr::consensusResult, stateStr);
if (result_->state == ConsensusState::No)
{
CLOG(clog) << "No consensus. ";
@@ -1872,35 +1914,6 @@ Consensus<Adaptor>::haveConsensus(std::unique_ptr<std::stringstream> const& clog
CLOG(clog) << "Unable to reach consensus " << json::Compact{getJson(true)} << ". ";
}
span.setAttribute(consensus::span::attr::agreeCount, static_cast<int64_t>(agree));
span.setAttribute(consensus::span::attr::disagreeCount, static_cast<int64_t>(disagree));
span.setAttribute(
consensus::span::attr::convergePercent, static_cast<int64_t>(convergePercent_));
span.setAttribute(consensus::span::attr::haveCloseTimeConsensus, haveCloseTimeConsensus_);
span.setAttribute(
consensus::span::attr::thresholdPercent,
static_cast<int64_t>(adaptor_.parms().avCtConsensusPct));
span.setAttribute(
consensus::span::attr::proposersFinished, static_cast<int64_t>(currentFinished));
span.setAttribute(consensus::span::attr::consensusStalled, stalled);
span.setAttribute(
consensus::span::attr::establishCounter, static_cast<int64_t>(establishCounter_));
char const* stateStr = "no";
if (result_->state == ConsensusState::Yes)
{
stateStr = "yes";
}
else if (result_->state == ConsensusState::MovedOn)
{
stateStr = "moved_on";
}
else if (result_->state == ConsensusState::Expired)
{
stateStr = "expired";
}
span.setAttribute(consensus::span::attr::consensusResult, stateStr);
CLOG(clog) << "Consensus has been reached. ";
// NOLINTEND(bugprone-unchecked-optional-access)
return true;

View File

@@ -238,6 +238,10 @@ inline constexpr auto expired = makeStr("expired");
inline constexpr auto increased = makeStr("increased");
inline constexpr auto decreased = makeStr("decreased");
inline constexpr auto unchanged = makeStr("unchanged");
// consensus_phase attribute values (the phase the round is entering).
inline constexpr auto phaseOpen = makeStr("open");
inline constexpr auto phaseEstablish = makeStr("establish");
inline constexpr auto phaseAccepted = makeStr("accepted");
} // namespace val
} // namespace xrpl::telemetry::consensus::span

View File

@@ -31,25 +31,19 @@
* span.setAttribute(...);
* @endcode
*
* @note These span names use inline string_view literals. When
* ConsensusSpanNames.h (from Phase 4) is available, callers should
* migrate to using the constexpr constants defined there.
* @note Span names come from the canonical constants in
* ConsensusSpanNames.h (consensus::span::proposalReceive /
* validationReceive) so they stay in sync with the rest of Phase 4.
*/
#include <xrpld/consensus/ConsensusSpanNames.h>
#include <xrpl/proto/xrpl.pb.h>
#include <xrpl/telemetry/SpanGuard.h>
#include <xrpl/telemetry/TraceContextValidation.h>
namespace xrpl::telemetry {
// Inline span name constants for consensus receive spans.
// Phase 4 will provide these via ConsensusSpanNames.h; these are
// temporary definitions for the propagation infrastructure.
namespace detail {
inline constexpr std::string_view proposalReceiveName = "consensus.proposal.receive";
inline constexpr std::string_view validationReceiveName = "consensus.validation.receive";
} // namespace detail
/** Create a "consensus.proposal.receive" span for an incoming proposal.
*
* If the message carries a TraceContext with a valid span_id, the
@@ -75,7 +69,7 @@ proposalReceiveSpan([[maybe_unused]] protocol::TMProposeSet const& msg)
// trace_id so the receiving span shares the same trace.
return SpanGuard::hashSpan(
TraceCategory::Consensus,
detail::proposalReceiveName,
consensus::span::proposalReceive,
reinterpret_cast<std::uint8_t const*>(tc.trace_id().data()),
tc.trace_id().size(),
reinterpret_cast<std::uint8_t const*>(tc.span_id().data()),
@@ -86,7 +80,8 @@ proposalReceiveSpan([[maybe_unused]] protocol::TMProposeSet const& msg)
}
#endif
// No propagated context — create a standalone span.
return SpanGuard::span(TraceCategory::Consensus, "consensus", "proposal.receive");
return SpanGuard::span(
TraceCategory::Consensus, seg::consensus, consensus::span::op::proposalReceive);
}
/** Create a "consensus.validation.receive" span for an incoming validation.
@@ -111,7 +106,7 @@ validationReceiveSpan([[maybe_unused]] protocol::TMValidation const& msg)
{
return SpanGuard::hashSpan(
TraceCategory::Consensus,
detail::validationReceiveName,
consensus::span::validationReceive,
reinterpret_cast<std::uint8_t const*>(tc.trace_id().data()),
tc.trace_id().size(),
reinterpret_cast<std::uint8_t const*>(tc.span_id().data()),
@@ -122,7 +117,8 @@ validationReceiveSpan([[maybe_unused]] protocol::TMValidation const& msg)
}
#endif
// No propagated context — create a standalone span.
return SpanGuard::span(TraceCategory::Consensus, "consensus", "validation.receive");
return SpanGuard::span(
TraceCategory::Consensus, seg::consensus, consensus::span::op::validationReceive);
}
} // namespace xrpl::telemetry