From 5b7081c3b70bdd9ba33ea0fb616e9a67a36b0da8 Mon Sep 17 00:00:00 2001 From: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com> Date: Thu, 30 Jul 2026 19:47:58 +0100 Subject: [PATCH] fix(telemetry): start tracing before the first spans are emitted telemetry_->start() ran at the end of ApplicationImp::start(), after overlay_->start(). Spans are emitted well before that, during setup(): beginConsensus() runs the first consensus round there. SpanGuard drops a span whenever the global Telemetry instance is not yet live, so that round's spans were never recorded. Move the start into setup(), behind a new startTelemetry() seam, right after the node identity is known. getNodeIdentity() needs only the cmdline, the config, or the wallet DB, and initRelationalDatabase() has already created the latter -- the adjacent peerReservations_ load proves it is usable -- so the identity block moves up with it. The new position is bounded on both sides: after initRelationalDatabase() because the identity needs the wallet DB and a DB failure aborts setup(), and before beginConsensus() because that emits the first spans. --- src/xrpld/app/main/Application.cpp | 52 ++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/src/xrpld/app/main/Application.cpp b/src/xrpld/app/main/Application.cpp index c8de63f914..9494144f48 100644 --- a/src/xrpld/app/main/Application.cpp +++ b/src/xrpld/app/main/Application.cpp @@ -1141,6 +1141,22 @@ private: void startGenesisLedger(); + /** + * Start the tracing pipeline. + * + * Called once from setup(), as soon as the node identity is known. + * Starting here rather than in start() means spans emitted during the + * rest of setup() are recorded: SpanGuard drops a span whenever the + * global Telemetry instance is not yet live, and the first consensus + * round runs inside setup(). + * + * @pre nodeIdentity_ is populated, so setServiceInstanceId() has + * already supplied the service.instance.id resource attribute + * (the Telemetry resource is fixed once start() builds it). + */ + void + startTelemetry(); + std::shared_ptr getLastFullLedger(); @@ -1227,6 +1243,27 @@ ApplicationImp::setup(boost::program_options::variables_map const& cmdline) return false; } + nodeIdentity_ = getNodeIdentity(*this, cmdline); + + // Now that the node identity is known, inject it into the telemetry + // resource attributes — but only if the user didn't already set a + // custom service_instance_id in [telemetry]. The Telemetry object + // was constructed with an empty serviceInstanceId because + // nodeIdentity_ is not available in the member initializer list. + if (!config_->section("telemetry").exists("service_instance_id")) + telemetry_->setServiceInstanceId(toBase58(TokenType::NodePublic, nodeIdentity_->first)); + + // Start telemetry here, not in start(). Spans are emitted during the rest + // of setup() — the first consensus round in beginConsensus() below — and + // are dropped unless the global Telemetry instance is already live. + // + // The position is bounded on both sides: + // - After initRelationalDatabase(): the wallet DB must exist for the node + // identity above, and a DB failure aborts setup(), so starting earlier + // would export a partial trace stream for a run that never comes up. + // - Before beginConsensus(): that call emits the first consensus spans. + startTelemetry(); + if (validatorKeys_.keys) setMaxDisallowedLedger(); @@ -1314,16 +1351,6 @@ ApplicationImp::setup(boost::program_options::variables_map const& cmdline) orderBookDB_->setup(getLedgerMaster().getCurrentLedger()); - nodeIdentity_ = getNodeIdentity(*this, cmdline); - - // Now that the node identity is known, inject it into the telemetry - // resource attributes — but only if the user didn't already set a - // custom service_instance_id in [telemetry]. The Telemetry object - // was constructed with an empty serviceInstanceId because - // nodeIdentity_ is not available in the member initializer list. - if (!config_->section("telemetry").exists("service_instance_id")) - telemetry_->setServiceInstanceId(toBase58(TokenType::NodePublic, nodeIdentity_->first)); - if (!cluster_->load(config().section(Sections::kClusterNodes))) { JLOG(journal_.fatal()) << "Invalid entry in cluster configuration."; @@ -1536,6 +1563,11 @@ ApplicationImp::start(bool withTimers) ledgerCleaner_->start(); perfLog_->start(); +} + +void +ApplicationImp::startTelemetry() +{ telemetry_->start(); }