feat(telemetry): enrich TX and TxQ spans with tx_type, fee, sequence, and status

Adds workflow-identifying attributes to transaction lifecycle spans:
- tx.process: tx_type, fee (drops), sequence
- tx.receive: tx_type
- txq.enqueue: tx_type
- txq.accept.tx: txq_status (applied/failed/retried)
- txq.accept: ledger_changed

Enables filtering traces by transaction type (Payment, AMMDeposit, etc.)
and understanding TxQ outcomes without correlating tx_hash externally.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Pratik Mankawde
2026-06-03 15:52:21 +01:00
parent d5f9242f84
commit ebf107e73c
5 changed files with 31 additions and 0 deletions

View File

@@ -1334,6 +1334,15 @@ NetworkOPsImp::processTransaction(
auto span = std::make_shared<SpanGuard>(txProcessSpan(transaction->getID()));
span->setAttribute(tx_span::attr::txHash, to_string(transaction->getID()).c_str());
span->setAttribute(tx_span::attr::local, bLocal);
if (auto const& stx = transaction->getSTransaction())
{
if (auto const* fmt = TxFormats::getInstance().findByType(stx->getTxnType()))
span->setAttribute(tx_span::attr::txType, fmt->getName().c_str());
span->setAttribute(
tx_span::attr::fee, static_cast<int64_t>(stx->getFieldAmount(sfFee).xrp().drops()));
span->setAttribute(
tx_span::attr::sequence, static_cast<int64_t>(stx->getSeqProxy().value()));
}
auto ev = jobQueue_.makeLoadEvent(JtTxnProc, "ProcessTXN");

View File

@@ -55,6 +55,16 @@ inline constexpr auto suppressed = makeStr("suppressed");
inline constexpr auto txStatus = makeStr("tx_status");
/// "peer_version" — version of peer that sent the tx.
inline constexpr auto peerVersion = makeStr("peer_version");
/// "tx_type" — transaction type name (e.g., "Payment", "OfferCreate").
inline constexpr auto txType = makeStr("tx_type");
/// "fee" — transaction fee in drops.
inline constexpr auto fee = makeStr("fee");
/// "sequence" — transaction sequence number.
inline constexpr auto sequence = makeStr("sequence");
/// "ter_result" — engine result code after application.
inline constexpr auto terResult = makeStr("ter_result");
/// "applied" — whether the transaction was applied to the ledger.
inline constexpr auto applied = makeStr("applied");
} // namespace attr
// ===== Attribute values ====================================================

View File

@@ -28,6 +28,7 @@
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/SeqProxy.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/TxFormats.h>
#include <xrpl/protocol/Units.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/protocol/jss.h>
@@ -741,6 +742,8 @@ TxQ::apply(
auto span =
SpanGuard::span(TraceCategory::Transactions, txq_span::prefix::txq, txq_span::op::enqueue);
span.setAttribute(txq_span::attr::txHash, to_string(tx->getTransactionID()).c_str());
if (auto const* fmt = TxFormats::getInstance().findByType(tx->getTxnType()))
span.setAttribute(txq_span::attr::txType, fmt->getName().c_str());
NumberSO const stNumberSO{view.rules().enabled(fixUniversalNumber)};
@@ -1477,6 +1480,7 @@ TxQ::accept(Application& app, OpenView& view)
if (didApply)
{
txSpan.setAttribute(txq_span::attr::txqStatus, txq_span::val::applied);
// Remove the candidate from the queue
JLOG(j_.debug()) << "Queued transaction " << candidateIter->txID
<< " applied successfully with " << transToken(txnResult)
@@ -1497,12 +1501,14 @@ TxQ::accept(Application& app, OpenView& view)
{
account.dropPenalty = true;
}
txSpan.setAttribute(txq_span::attr::txqStatus, txq_span::val::failed);
JLOG(j_.debug()) << "Queued transaction " << candidateIter->txID << " failed with "
<< transToken(txnResult) << ". Remove from queue.";
candidateIter = eraseAndAdvance(candidateIter);
}
else
{
txSpan.setAttribute(txq_span::attr::txqStatus, txq_span::val::retried);
JLOG(j_.debug()) << "Queued transaction " << candidateIter->txID << " failed with "
<< transToken(txnResult) << ". Leave in queue."
<< " Applied: " << didApply << ". Flags: " << candidateIter->flags;
@@ -1598,6 +1604,7 @@ TxQ::accept(Application& app, OpenView& view)
}
}
XRPL_ASSERT(byFee_.size() == startingSize, "xrpl::TxQ::accept : byFee size match");
span.setAttribute(txq_span::attr::ledgerChanged, ledgerChanged);
return ledgerChanged;
}

View File

@@ -93,6 +93,8 @@ inline constexpr auto terCode = makeStr("ter_code");
inline constexpr auto retriesRemaining = makeStr("retries_remaining");
/// "num_cleared" — entries cleared in batch.
inline constexpr auto numCleared = makeStr("num_cleared");
/// "tx_type" — transaction type name (e.g., "Payment", "OfferCreate").
inline constexpr auto txType = makeStr("tx_type");
} // namespace attr
// ===== Attribute values ====================================================

View File

@@ -52,6 +52,7 @@
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/Serializer.h>
#include <xrpl/protocol/TxFlags.h>
#include <xrpl/protocol/TxFormats.h>
#include <xrpl/protocol/digest.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol/tokens.h>
@@ -1329,6 +1330,8 @@ PeerImp::handleTransaction(
auto span = std::make_shared<SpanGuard>(txReceiveSpan(txID, *m));
span->setAttribute(tx_span::attr::txHash, to_string(txID).c_str());
span->setAttribute(tx_span::attr::peerId, static_cast<int64_t>(id_));
if (auto const* fmt = TxFormats::getInstance().findByType(stx->getTxnType()))
span->setAttribute(tx_span::attr::txType, fmt->getName().c_str());
if (auto const version = getVersion(); !version.empty())
span->setAttribute(tx_span::attr::peerVersion, version.c_str());
// Set defaults for conditional attributes so they are always present