diff --git a/OpenTelemetryPlan/02-design-decisions.md b/OpenTelemetryPlan/02-design-decisions.md index 10c0707156..7468d204bb 100644 --- a/OpenTelemetryPlan/02-design-decisions.md +++ b/OpenTelemetryPlan/02-design-decisions.md @@ -352,15 +352,15 @@ Establish-phase gap fill and cross-node correlation attributes (Phase 4a): #### Ledger & Job Attributes -| Key | Type | Description | -| ----------------- | ------- | --------------------- | -| `ledger_hash` | string | Ledger hash | -| `ledger_index` | int64 | Ledger sequence/index | -| `close_time` | int64 | Close time (epoch) | -| `ledger_tx_count` | int64 | Transaction count | -| `job_type` | string | Job type name | -| `job_queue_ms` | float64 | Time spent in queue | -| `job_worker` | int64 | Worker thread ID | +| Key | Type | Description | +| --------------------------- | ------- | --------------------------------- | +| `ledger_hash` | string | Ledger hash | +| `ledger_index` | int64 | Ledger sequence/index | +| `close_time_ripple_epoch_s` | int64 | Close time (Ripple epoch seconds) | +| `ledger_tx_count` | int64 | Transaction count | +| `job_type` | string | Job type name | +| `job_queue_ms` | float64 | Time spent in queue | +| `job_worker` | int64 | Worker thread ID | #### PathFinding Attributes diff --git a/OpenTelemetryPlan/06-implementation-phases.md b/OpenTelemetryPlan/06-implementation-phases.md index 18435686d3..2e22fe222a 100644 --- a/OpenTelemetryPlan/06-implementation-phases.md +++ b/OpenTelemetryPlan/06-implementation-phases.md @@ -219,14 +219,14 @@ SHAMap tracing are not implemented. ### Spans Produced -| Span Name | Location | Attributes | -| --------------------------- | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `consensus.phase.open` | `Consensus.h` | _(none)_ | -| `consensus.proposal.send` | `RCLConsensus.cpp` | `consensus_round` | -| `consensus.ledger_close` | `RCLConsensus.cpp` | `ledger_seq`, `consensus_mode` | -| `consensus.accept` | `RCLConsensus.cpp` | `proposers`, `round_time_ms`, `quorum` | -| `consensus.accept.apply` | `RCLConsensus.cpp` | `close_time`, `close_time_correct`, `close_resolution_ms`, `consensus_state`, `proposing`, `round_time_ms`, `ledger_seq`, `parent_close_time`, `close_time_self`, `close_time_vote_bins`, `resolution_direction` | -| `consensus.validation.send` | `RCLConsensus.cpp` | `ledger_seq`, `proposing` | +| Span Name | Location | Attributes | +| --------------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `consensus.phase.open` | `Consensus.h` | _(none)_ | +| `consensus.proposal.send` | `RCLConsensus.cpp` | `consensus_round` | +| `consensus.ledger_close` | `RCLConsensus.cpp` | `ledger_seq`, `consensus_mode` | +| `consensus.accept` | `RCLConsensus.cpp` | `proposers`, `round_time_ms`, `quorum` | +| `consensus.accept.apply` | `RCLConsensus.cpp` | `close_time_ripple_epoch_s`, `close_time_correct`, `close_resolution_ms`, `consensus_state`, `proposing`, `round_time_ms`, `ledger_seq`, `parent_close_time_ripple_epoch_s`, `close_time_self_ripple_epoch_s`, `close_time_vote_bins`, `resolution_direction` | +| `consensus.validation.send` | `RCLConsensus.cpp` | `ledger_seq`, `proposing` | ### Exit Criteria diff --git a/OpenTelemetryPlan/09-data-collection-reference.md b/OpenTelemetryPlan/09-data-collection-reference.md index 3f9e739038..0e2a69139d 100644 --- a/OpenTelemetryPlan/09-data-collection-reference.md +++ b/OpenTelemetryPlan/09-data-collection-reference.md @@ -391,63 +391,63 @@ Join a transaction's work to its ledger with `{span.current_ledger_seq=}`. #### Consensus Attributes -| Attribute | Type | Set On | Description | -| ---------------------------- | ------- | -------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- | -| `consensus_ledger_id` | string | `consensus.round` | Previous-ledger id anchoring the round | -| `ledger_seq` | int64 | `consensus.round`, `consensus.ledger_close`, `consensus.accept.apply`, `consensus.validation.send` | Ledger sequence number | -| `consensus_mode` | string | `consensus.round`, `consensus.ledger_close` | Node mode: `"Proposing"`, `"Observing"`, `"Wrong"`, etc. | -| `consensus_round_id` | int64 | `consensus.round` | Round identifier | -| `consensus_phase` | string | `consensus.round` | Current phase name (updated on each transition) | -| `trace_strategy` | string | `consensus.round` | Trace-id strategy (`deterministic` / `attribute`) | -| `previous_ledger_seq` | int64 | `consensus.round` | Sequence of the previous ledger | -| `previous_proposers` | int64 | `consensus.round` | Proposer count in the previous round | -| `previous_round_time_ms` | int64 | `consensus.round` | Duration of the previous round | -| `consensus_round` | int64 | `consensus.proposal.send` | Proposal sequence number for the broadcast proposal | -| `is_bow_out` | boolean | `consensus.proposal.send` | Whether the proposal is a bow-out (resigning the round) | -| `tx_count_open` | int64 | `consensus.ledger_close` | Transactions in the open ledger at close | -| `close_time_resolution_ms` | int64 | `consensus.ledger_close` | Close-time rounding granularity | -| `start_reason` | string | `consensus.phase.open` | Entry path: `"initial"` or `"recovered"` | -| `previous_close_agree` | boolean | `consensus.phase.open` | Whether the prior ledger's close time was agreed | -| `peer_positions_at_open` | int64 | `consensus.phase.open` | Positions held after buffered proposals are replayed | -| `early_close_triggered` | boolean | `consensus.phase.open` | Round skipped the timer because peers had already closed | -| `tx_sets_acquired` | int64 | `consensus.phase.open` | Peer transaction sets held at close, excluding our own | -| `close_reason` | string | `consensus.phase.open` | `"anomaly"`, `"others_closed"`, `"idle"`, or `"normal"` | -| `proposers_validated` | int64 | `consensus.phase.open` | Trusted validators of the previous ledger, at close | -| `converge_percent` | int64 | `consensus.establish`, `consensus.update_positions`, `consensus.check` | Convergence percentage | -| `establish_count` | int64 | `consensus.establish`, `consensus.check` | Establish-phase iteration count | -| `close_time_avalanche_state` | string | `consensus.establish` | Terminal regime: `"init"`, `"mid"`, `"late"`, or `"stuck"` | -| `proposers` | int64 | `consensus.establish`, `consensus.update_positions`, `consensus.accept` | Number of proposers | -| `disputes_count` | int64 | `consensus.establish`, `consensus.update_positions` | Number of disputed transactions | -| `tx_id` | string | `consensus.update_positions` | Disputed transaction id (per-dispute event) | -| `dispute_our_vote` | boolean | `consensus.update_positions` | Our vote on the disputed tx | -| `dispute_yays` | int64 | `consensus.update_positions` | Yes votes on the disputed tx | -| `dispute_nays` | int64 | `consensus.update_positions` | No votes on the disputed tx | -| `avalanche_threshold` | int64 | `consensus.update_positions` | Escalated weight needed to change our vote | -| `close_time_threshold` | int64 | `consensus.update_positions` | Close-time agreement threshold percentage | -| `agree_count` | int64 | `consensus.check` | Agreeing proposer count | -| `disagree_count` | int64 | `consensus.check` | Disagreeing proposer count | -| `threshold_percent` | int64 | `consensus.check` | Agreement threshold percentage | -| `have_close_time_consensus` | boolean | `consensus.update_positions`, `consensus.check` | Whether the close time reached consensus | -| `proposers_finished` | int64 | `consensus.check` | Proposers that have already validated the next ledger | -| `consensus_stalled` | boolean | `consensus.check` | Whether `checkConsensus` reported a stall | -| `consensus_result` | string | `consensus.check` | Check outcome | -| `quorum` | int64 | `consensus.accept` | Quorum required | -| `round_time_ms` | int64 | `consensus.accept`, `consensus.accept.apply` | Total consensus round duration in milliseconds | -| `consensus_state` | string | `consensus.accept.apply` | Consensus outcome: `"finished"` or `"moved_on"` | -| `close_time` | int64 | `consensus.accept.apply` | Agreed-upon ledger close time (epoch seconds) | -| `close_time_correct` | boolean | `consensus.accept.apply` | Whether validators agreed on close time | -| `close_resolution_ms` | int64 | `consensus.accept.apply` | Close-time rounding granularity in milliseconds | -| `proposing` | boolean | `consensus.accept.apply`, `consensus.validation.send` | Whether this node was a proposer | -| `parent_close_time` | int64 | `consensus.accept.apply` | Parent ledger close time | -| `close_time_self` | int64 | `consensus.accept.apply` | This node's close-time vote | -| `close_time_vote_bins` | string | `consensus.accept.apply` | Distribution of close-time votes | -| `resolution_direction` | string | `consensus.accept.apply` | Whether close resolution increased/decreased/unchanged | -| `tx_count` | int64 | `consensus.accept.apply` | Transactions in the accepted set | -| `ledger_hash` | string | `consensus.validation.send` | Full hash of the validated ledger (shared with peer) | -| `full_validation` | boolean | `consensus.validation.send` | Whether this is a full validation | -| `validation_sign_time` | int64 | `consensus.validation.send` | Validation signing time | -| `mode_old` | string | `consensus.mode_change` | Operating mode before the transition | -| `mode_new` | string | `consensus.mode_change` | Operating mode after the transition | +| Attribute | Type | Set On | Description | +| ---------------------------------- | ------- | -------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- | +| `consensus_ledger_id` | string | `consensus.round` | Previous-ledger id anchoring the round | +| `ledger_seq` | int64 | `consensus.round`, `consensus.ledger_close`, `consensus.accept.apply`, `consensus.validation.send` | Ledger sequence number | +| `consensus_mode` | string | `consensus.round`, `consensus.ledger_close` | Node mode: `"Proposing"`, `"Observing"`, `"Wrong"`, etc. | +| `consensus_round_id` | int64 | `consensus.round` | Round identifier | +| `consensus_phase` | string | `consensus.round` | Current phase name (updated on each transition) | +| `trace_strategy` | string | `consensus.round` | Trace-id strategy (`deterministic` / `attribute`) | +| `previous_ledger_seq` | int64 | `consensus.round` | Sequence of the previous ledger | +| `previous_proposers` | int64 | `consensus.round` | Proposer count in the previous round | +| `previous_round_time_ms` | int64 | `consensus.round` | Duration of the previous round | +| `consensus_round` | int64 | `consensus.proposal.send` | Proposal sequence number for the broadcast proposal | +| `is_bow_out` | boolean | `consensus.proposal.send` | Whether the proposal is a bow-out (resigning the round) | +| `tx_count_open` | int64 | `consensus.ledger_close` | Transactions in the open ledger at close | +| `close_time_resolution_ms` | int64 | `consensus.ledger_close` | Close-time rounding granularity | +| `start_reason` | string | `consensus.phase.open` | Entry path: `"initial"` or `"recovered"` | +| `previous_close_agree` | boolean | `consensus.phase.open` | Whether the prior ledger's close time was agreed | +| `peer_positions_at_open` | int64 | `consensus.phase.open` | Positions held after buffered proposals are replayed | +| `early_close_triggered` | boolean | `consensus.phase.open` | Round skipped the timer because peers had already closed | +| `tx_sets_acquired` | int64 | `consensus.phase.open` | Peer transaction sets held at close, excluding our own | +| `close_reason` | string | `consensus.phase.open` | `"anomaly"`, `"others_closed"`, `"idle"`, or `"normal"` | +| `proposers_validated` | int64 | `consensus.phase.open` | Trusted validators of the previous ledger, at close | +| `converge_percent` | int64 | `consensus.establish`, `consensus.update_positions`, `consensus.check` | Convergence percentage | +| `establish_count` | int64 | `consensus.establish`, `consensus.check` | Establish-phase iteration count | +| `close_time_avalanche_state` | string | `consensus.establish` | Terminal regime: `"init"`, `"mid"`, `"late"`, or `"stuck"` | +| `proposers` | int64 | `consensus.establish`, `consensus.update_positions`, `consensus.accept` | Number of proposers | +| `disputes_count` | int64 | `consensus.establish`, `consensus.update_positions` | Number of disputed transactions | +| `tx_id` | string | `consensus.update_positions` | Disputed transaction id (per-dispute event) | +| `dispute_our_vote` | boolean | `consensus.update_positions` | Our vote on the disputed tx | +| `dispute_yays` | int64 | `consensus.update_positions` | Yes votes on the disputed tx | +| `dispute_nays` | int64 | `consensus.update_positions` | No votes on the disputed tx | +| `avalanche_threshold` | int64 | `consensus.update_positions` | Escalated weight needed to change our vote | +| `close_time_threshold` | int64 | `consensus.update_positions` | Close-time agreement threshold percentage | +| `agree_count` | int64 | `consensus.check` | Agreeing proposer count | +| `disagree_count` | int64 | `consensus.check` | Disagreeing proposer count | +| `threshold_percent` | int64 | `consensus.check` | Agreement threshold percentage | +| `have_close_time_consensus` | boolean | `consensus.update_positions`, `consensus.check` | Whether the close time reached consensus | +| `proposers_finished` | int64 | `consensus.check` | Proposers that have already validated the next ledger | +| `consensus_stalled` | boolean | `consensus.check` | Whether `checkConsensus` reported a stall | +| `consensus_result` | string | `consensus.check` | Check outcome | +| `quorum` | int64 | `consensus.accept` | Quorum required | +| `round_time_ms` | int64 | `consensus.accept`, `consensus.accept.apply` | Total consensus round duration in milliseconds | +| `consensus_state` | string | `consensus.accept.apply` | Consensus outcome: `"finished"` or `"moved_on"` | +| `close_time_ripple_epoch_s` | int64 | `consensus.accept.apply` | Agreed-upon ledger close time (Ripple epoch seconds) | +| `close_time_correct` | boolean | `consensus.accept.apply` | Whether validators agreed on close time | +| `close_resolution_ms` | int64 | `consensus.accept.apply` | Close-time rounding granularity in milliseconds | +| `proposing` | boolean | `consensus.accept.apply`, `consensus.validation.send` | Whether this node was a proposer | +| `parent_close_time_ripple_epoch_s` | int64 | `consensus.accept.apply` | Parent ledger close time | +| `close_time_self_ripple_epoch_s` | int64 | `consensus.accept.apply` | This node's close-time vote | +| `close_time_vote_bins` | string | `consensus.accept.apply` | Distribution of close-time votes | +| `resolution_direction` | string | `consensus.accept.apply` | Whether close resolution increased/decreased/unchanged | +| `tx_count` | int64 | `consensus.accept.apply` | Transactions in the accepted set | +| `ledger_hash` | string | `consensus.validation.send` | Full hash of the validated ledger (shared with peer) | +| `full_validation` | boolean | `consensus.validation.send` | Whether this is a full validation | +| `validation_sign_time` | int64 | `consensus.validation.send` | Validation signing time | +| `mode_old` | string | `consensus.mode_change` | Operating mode before the transition | +| `mode_new` | string | `consensus.mode_change` | Operating mode after the transition | > **`quorum` is on `consensus.accept` only.** Its single set site is > `RCLConsensus::Adaptor::makeAcceptSpan()` @@ -478,19 +478,19 @@ Join a transaction's work to its ledger with `{span.current_ledger_seq=}`. #### Ledger Attributes -| Attribute | Type | Set On | Description | -| --------------------- | ------- | ------------------------------------------------- | ------------------------------------------------ | -| `ledger_seq` | int64 | `ledger.build`, `ledger.validate`, `ledger.store` | Ledger sequence number | -| `close_time` | int64 | `ledger.build` | Ledger close time (epoch seconds) | -| `close_time_correct` | boolean | `ledger.build` | Whether close time was agreed upon by validators | -| `close_resolution_ms` | int64 | `ledger.build` | Close time rounding granularity in milliseconds | -| `tx_count` | int64 | `tx.apply` | Transactions applied to the ledger | -| `tx_failed` | int64 | `tx.apply` | Failed transactions in the apply set | -| `validations` | int64 | `ledger.validate` | Number of validations received for this ledger | -| `acquire_reason` | string | `ledger.acquire` | Fetch trigger (`history`/`consensus`/`generic`) | -| `timeouts` | int64 | `ledger.acquire` | Number of fetch timeouts | -| `peer_count` | int64 | `ledger.acquire` | Peers queried during the fetch | -| `outcome` | string | `ledger.acquire` | Fetch outcome (`complete`/`failed`/`aborted`) | +| Attribute | Type | Set On | Description | +| --------------------------- | ------- | ------------------------------------------------- | ------------------------------------------------ | +| `ledger_seq` | int64 | `ledger.build`, `ledger.validate`, `ledger.store` | Ledger sequence number | +| `close_time_ripple_epoch_s` | int64 | `ledger.build` | Ledger close time (Ripple epoch seconds) | +| `close_time_correct` | boolean | `ledger.build` | Whether close time was agreed upon by validators | +| `close_resolution_ms` | int64 | `ledger.build` | Close time rounding granularity in milliseconds | +| `tx_count` | int64 | `tx.apply` | Transactions applied to the ledger | +| `tx_failed` | int64 | `tx.apply` | Failed transactions in the apply set | +| `validations` | int64 | `ledger.validate` | Number of validations received for this ledger | +| `acquire_reason` | string | `ledger.acquire` | Fetch trigger (`history`/`consensus`/`generic`) | +| `timeouts` | int64 | `ledger.acquire` | Number of fetch timeouts | +| `peer_count` | int64 | `ledger.acquire` | Peers queried during the fetch | +| `outcome` | string | `ledger.acquire` | Fetch outcome (`complete`/`failed`/`aborted`) | The apply-step span `tx.apply` (child of `ledger.build`) carries `tx_count`/`tx_failed`; the parent `ledger.build` carries `ledger_seq` and the close-time attributes. diff --git a/OpenTelemetryPlan/Phase4_taskList.md b/OpenTelemetryPlan/Phase4_taskList.md index beed519c96..57c9ad0725 100644 --- a/OpenTelemetryPlan/Phase4_taskList.md +++ b/OpenTelemetryPlan/Phase4_taskList.md @@ -381,27 +381,27 @@ Phase 7's `ValidationTracker` builds metric-level aggregation (1h/24h agreement ### Implemented Spans -| Span Name | Method | Key Attributes | -| --------------------------- | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `consensus.proposal.send` | `Adaptor::propose` | `consensus_round`, `is_bow_out` | -| `consensus.ledger_close` | `Adaptor::onClose` | `ledger_seq`, `consensus_mode` | -| `consensus.accept` | `Adaptor::onAccept` | `proposers`, `round_time_ms`, `quorum`, `disputes_count`, `consensus_state` | -| `consensus.accept.apply` | `Adaptor::doAccept` | `close_time`, `close_time_correct`, `close_resolution_ms`, `consensus_state`, `proposing`, `round_time_ms`, `ledger_seq`, `parent_close_time`, `close_time_self`, `close_time_vote_bins`, `resolution_direction` | -| `consensus.validation.send` | `Adaptor::onAccept` (via validate) | `proposing`, `ledger_hash`, `ledger_seq`, `full_validation`, `validation_sign_time` | +| Span Name | Method | Key Attributes | +| --------------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `consensus.proposal.send` | `Adaptor::propose` | `consensus_round`, `is_bow_out` | +| `consensus.ledger_close` | `Adaptor::onClose` | `ledger_seq`, `consensus_mode` | +| `consensus.accept` | `Adaptor::onAccept` | `proposers`, `round_time_ms`, `quorum`, `disputes_count`, `consensus_state` | +| `consensus.accept.apply` | `Adaptor::doAccept` | `close_time_ripple_epoch_s`, `close_time_correct`, `close_resolution_ms`, `consensus_state`, `proposing`, `round_time_ms`, `ledger_seq`, `parent_close_time_ripple_epoch_s`, `close_time_self_ripple_epoch_s`, `close_time_vote_bins`, `resolution_direction` | +| `consensus.validation.send` | `Adaptor::onAccept` (via validate) | `proposing`, `ledger_hash`, `ledger_seq`, `full_validation`, `validation_sign_time` | #### Close Time Attributes (consensus.accept.apply) The `consensus.accept.apply` span captures ledger close time agreement details driven by `avCT_CONSENSUS_PCT` (75% validator agreement threshold): -- **`close_time`** — Agreed-upon ledger close time (epoch seconds). When validators disagree (`consensusCloseTime == epoch`), this is synthetically set to `prevCloseTime + 1s`. +- **`close_time_ripple_epoch_s`** — Agreed-upon ledger close time (Ripple epoch seconds). When validators disagree (`consensusCloseTime == epoch`), this is synthetically set to `prevCloseTime + 1s`. - **`close_time_correct`** — `true` if validators reached agreement, `false` if they "agreed to disagree" (close time forced to prev+1s). - **`close_resolution_ms`** — Rounding granularity for close time (starts at 30s, decreases as ledger interval stabilizes). - **`consensus_state`** — `"finished"` (normal) or `"moved_on"` (consensus failed, adopted best available). - **`proposing`** — Whether this node was proposing. - **`round_time_ms`** — Total consensus round duration. -- **`parent_close_time`** — Previous ledger's close time (epoch seconds). Enables computing close-time deltas across consecutive rounds without correlating separate spans. -- **`close_time_self`** — This node's own proposed close time before consensus voting. +- **`parent_close_time_ripple_epoch_s`** — Previous ledger's close time (Ripple epoch seconds). Enables computing close-time deltas across consecutive rounds without correlating separate spans. +- **`close_time_self_ripple_epoch_s`** — This node's own proposed close time before consensus voting. - **`close_time_vote_bins`** — Number of distinct close-time vote bins from peer proposals. Higher values indicate less agreement among validators. - **`resolution_direction`** — Whether close-time resolution `"increased"` (coarser), `"decreased"` (finer), or stayed `"unchanged"` relative to the previous ledger. diff --git a/docs/telemetry-runbook.md b/docs/telemetry-runbook.md index ba0d3f8917..4705c26ced 100644 --- a/docs/telemetry-runbook.md +++ b/docs/telemetry-runbook.md @@ -334,7 +334,7 @@ read it from the parent rather than filtering `tx.apply` on it. | `consensus.update_positions` | Consensus.h | `converge_percent`, `proposers`, `disputes_count`, `avalanche_threshold` (only when peer positions exist), `have_close_time_consensus`, `close_time_threshold` | Position update and dispute resolution (see Events below) | | `consensus.check` | Consensus.h | `agree_count`, `disagree_count`, `converge_percent`, `have_close_time_consensus`, `threshold_percent`, `proposers_finished`, `consensus_stalled`, `establish_count`, `consensus_result` | Consensus threshold check | | `consensus.accept` | RCLConsensus.cpp | `proposers`, `round_time_ms`, `quorum`, `disputes_count`, `consensus_state` | Ledger accepted by consensus | -| `consensus.accept.apply` | RCLConsensus.cpp | `ledger_seq`, `close_time`, `close_time_correct`, `close_resolution_ms`, `consensus_state`, `proposing`, `round_time_ms`, `parent_close_time`, `close_time_self`, `close_time_vote_bins`, `resolution_direction`, `tx_count`, `disputes_resolved_count` | Ledger application with close time details (see Events below) | +| `consensus.accept.apply` | RCLConsensus.cpp | `ledger_seq`, `close_time_ripple_epoch_s`, `close_time_correct`, `close_resolution_ms`, `consensus_state`, `proposing`, `round_time_ms`, `parent_close_time_ripple_epoch_s`, `close_time_self_ripple_epoch_s`, `close_time_vote_bins`, `resolution_direction`, `tx_count`, `disputes_resolved_count` | Ledger application with close time details (see Events below) | | `consensus.validation.send` | RCLConsensus.cpp | `ledger_seq`, `proposing`, `ledger_hash`, `full_validation`, `validation_sign_time` | Validation sent after accept (follows-from link) | | `consensus.mode_change` | RCLConsensus.cpp | `mode_old`, `mode_new` | Consensus mode transition | | `consensus.proposal.receive` | PeerImp.cpp | `proposal_trusted`, `consensus_round`, `prev_ledger_prefix`, `position_hash_prefix` | Proposal received from peer (extracts parent context from TraceContext when present; falls back to standalone span for older peers) | @@ -392,12 +392,12 @@ Span attributes are filtered with `span.` inside `{}`. Combine conditions ### Ledger Spans -| Span Name | Source File | Attributes | Description | -| ----------------- | ----------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | -| `ledger.build` | BuildLedger.cpp | `ledger_seq`, `close_time`, `close_time_correct`, `close_resolution_ms` | Ledger build during consensus | -| `ledger.validate` | LedgerMaster.cpp | `ledger_seq`, `validations` | Ledger promoted to validated | -| `ledger.store` | LedgerMaster.cpp | `ledger_seq` | Ledger stored in history | -| `ledger.acquire` | InboundLedger.cpp | `ledger_seq`, `acquire_reason`, `timeouts`, `peer_count`, `outcome` | Fetch a missing ledger from peers (parent varies — see [known issues](#where-telemetry-parenting-differs-from-protocol-flow)) | +| Span Name | Source File | Attributes | Description | +| ----------------- | ----------------- | -------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | +| `ledger.build` | BuildLedger.cpp | `ledger_seq`, `close_time_ripple_epoch_s`, `close_time_correct`, `close_resolution_ms` | Ledger build during consensus | +| `ledger.validate` | LedgerMaster.cpp | `ledger_seq`, `validations` | Ledger promoted to validated | +| `ledger.store` | LedgerMaster.cpp | `ledger_seq` | Ledger stored in history | +| `ledger.acquire` | InboundLedger.cpp | `ledger_seq`, `acquire_reason`, `timeouts`, `peer_count`, `outcome` | Fetch a missing ledger from peers (parent varies — see [known issues](#where-telemetry-parenting-differs-from-protocol-flow)) | `ledger.acquire` sets only `ledger_seq` and `acquire_reason` when the span opens in `init()`. `outcome` has three values, written on two different paths: