mirror of
https://github.com/XRPLF/rippled.git
synced 2026-09-27 15:28:03 +00:00
MetricsRegistry created its provider and synchronous instruments in start(), called from setup() after the node identity was read. Every XRPL_METRIC_* call site creates its instrument on first use, so any site that ran before that point found no meter and never recorded again. The start was moved three times to chase the newest early caller; nothing guaranteed the order. Build the pipeline in the constructor instead. ApplicationImp declares metricsRegistry_ right after telemetry_ and before every subsystem, so declaration order now guarantees the instruments exist before any producer. start() is gone and its config parsing moves to makeMetricsRegistryOptions(). The registry now guarantees a meter whenever it is enabled: the real one, or the OTel no-op meter if the pipeline failed to build. disablePipeline() owns that fallback and its one error log, and the constructor routes both std::exception and a non-std throw through it, because the SDK is third-party code. So the macros shrink to one function-local static built from meter() plus the record call: no once-flag, no null check, and no path for a call that arrives before the meter, because that state no longer exists. The three observable macros drop the same now-dead meter check. The lifecycle is three explicit phases with a Phase enum: Ready at construction, GaugesArmed by startAsyncGauges() once overlay_ exists, and Stopped by stop(). startAsyncGauges() checks the phase before the pipeline, so a second call and a call after stop() are each reported as what they are. run() stops both observers (insight collector, registry) before any service, and ~ApplicationImp repeats the stop for the setup() failure paths that never reach run(). stop() already detaches the callbacks, so it is the only call. Meter name and version come from kMeterName/kMeterVersion, and the endpoint default from Telemetry::Setup, so the two metric pipelines share one source for both.