feat(telemetry): correlate ledger.acquire outcome log via span activation

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Pratik Mankawde
2026-07-22 22:58:17 +01:00
parent 68bf633955
commit 28a8ab144b
2 changed files with 32 additions and 20 deletions

View File

@@ -152,9 +152,9 @@ later spans — the `stage` attribute identifies where it stopped.
> coroutine across `yield()` and resumes on whatever thread the scheduler picks.
> RPC, consensus, and transaction spans therefore keep per-line log-trace
> correlation, and their scopes are safe across coroutine yields. Job-handoff
> spans — transaction apply and receive, and consensus accept — are activated
> inside their worker bodies rather than at enqueue, so each worker's log lines
> carry the span's trace context.
> spans — transaction apply and receive, consensus accept, and ledger acquire —
> are activated inside their worker bodies rather than at enqueue, so each
> worker's log lines carry the span's trace context.
**Where to find**: Tempo → TraceQL: `{resource.service.name="xrpld" && name=~"tx.process|tx.receive"}`
or, for the apply pipeline: `{resource.service.name="xrpld" && name=~"tx.preflight|tx.preclaim|tx.transactor"}`

View File

@@ -453,25 +453,37 @@ InboundLedger::done()
touch();
// Finalize the acquire span with the outcome, timeout count, and peer
// count, then end it (reset) so its duration is exported.
if (acquireSpan_ && *acquireSpan_)
// count. Keep it active as the ambient context across the finalize and
// the outcome log below so that log line carries the span's trace_id.
// The activation is non-owning; acquireSpan_ still owns the span. The
// activation pops at the end of this block (restoring the prior context)
// while acquireSpan_ is still alive, then reset() ends the span.
{
using namespace telemetry;
acquireSpan_->setAttribute(
ledger_span::attr::outcome,
failed_ ? std::string_view(ledger_span::val::failed)
: std::string_view(ledger_span::val::complete));
acquireSpan_->setAttribute(ledger_span::attr::timeouts, static_cast<int64_t>(timeouts_));
acquireSpan_->setAttribute(
ledger_span::attr::peerCount, static_cast<int64_t>(getPeerCount()));
}
acquireSpan_.reset();
auto acquireActivation = (acquireSpan_ && *acquireSpan_) ? acquireSpan_->activate()
: telemetry::ScopedActivation{};
if (acquireSpan_ && *acquireSpan_)
{
using namespace telemetry;
acquireSpan_->setAttribute(
ledger_span::attr::outcome,
failed_ ? std::string_view(ledger_span::val::failed)
: std::string_view(ledger_span::val::complete));
acquireSpan_->setAttribute(
ledger_span::attr::timeouts, static_cast<int64_t>(timeouts_));
acquireSpan_->setAttribute(
ledger_span::attr::peerCount, static_cast<int64_t>(getPeerCount()));
}
JLOG(journal_.debug()) << "Acquire " << hash_ << (failed_ ? " fail " : " ")
<< ((timeouts_ == 0)
? std::string()
: (std::string("timeouts:") + std::to_string(timeouts_) + " "))
<< stats_.get();
JLOG(journal_.debug()) << "Acquire " << hash_ << (failed_ ? " fail " : " ")
<< ((timeouts_ == 0) ? std::string()
: (std::string("timeouts:") +
std::to_string(timeouts_) + " "))
<< stats_.get();
// acquireActivation pops here, before the span is ended below.
}
// End the acquire span after its outcome log. Unconditional so the span
// never leaks even when it was inactive.
acquireSpan_.reset();
XRPL_ASSERT(complete_ || failed_, "xrpl::InboundLedger::done : complete or failed");