fix(telemetry): order the metrics pipeline by instrument kind

beast::insight instruments are created during ApplicationImp's member-init
list, and opentelemetry-cpp 1.28 never rebinds an already-vended Meter, so an
instrument created before the MeterProvider is published records nothing for
the rest of the process. Observable instruments carry the opposite constraint:
registering one arms the SDK reader thread, and its callbacks run hook handlers
that read services which do not exist that early.

Publish the provider in Telemetry's constructor, ahead of every producer, and
defer only the observables. Collector gains onCollectionReady() and
onCollectionStopping(); OTelCollector arms and disarms its gauges in response.
StatsDCollector starts its polling thread in its own constructor and had the
same hazard, so it uses the pair to gate that thread.

The metrics resource carries service.instance.id and is immutable once built,
so the node public key is resolved in Main.cpp, where a config error can still
be reported, and passed to makeApplication(). getNodeIdentity() remains
authoritative; both paths now share readNodeIdentity(), so telemetry cannot
report a key the node has abandoned.

An explicit ~ApplicationImp stops observing and stops telemetry, covering the
setup() failure paths that never reach run(). Telemetry::stop() is once-only
and no longer clears another instance's global pointer. The histogram view's
meter selector now matches the meter actually in use, so its bucket boundaries
apply for the first time.
This commit is contained in:
Pratik Mankawde
2026-08-20 16:36:41 +01:00
parent e6688d8a0b
commit 4278014ab0
12 changed files with 580 additions and 125 deletions

View File

@@ -30,6 +30,29 @@ public:
virtual ~Collector() = 0;
/**
* Called once the services that hook handlers read are constructed.
*
* Implementations that poll their producers must not do so before this:
* hook handlers read live application state. Default is a no-op, for
* collectors that only push.
*/
virtual void
onCollectionReady()
{
}
/**
* Called before those services are shut down.
*
* Polling must have stopped by the time this returns. Paired with
* onCollectionReady().
*/
virtual void
onCollectionStopping()
{
}
/**
* Create a hook.
*

View File

@@ -16,6 +16,7 @@
#include <functional>
#include <memory>
#include <optional>
#include <string>
#include <unordered_set>
#include <utility>
@@ -88,6 +89,19 @@ addValidatorManifest(soci::session& session, std::string const& serialized);
void
clearNodeIdentity(soci::session& session);
/**
* Returns this node's stored keypair, if the database holds a valid one.
*
* Read-only: unlike getNodeIdentity(), never generates or persists a key. A row
* counts only when its public and secret keys are a pair.
*
* @param session Session with the database.
*
* @return The stored keypair, or std::nullopt.
*/
std::optional<std::pair<PublicKey, SecretKey>>
readNodeIdentity(soci::session& session);
/**
* Returns a stable public and private key for this node.
*