From 5f3aed978de69161d4d3dff1ef18068a29486ad0 Mon Sep 17 00:00:00 2001 From: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com> Date: Tue, 21 Jul 2026 19:59:58 +0100 Subject: [PATCH] docs: document XRPL_METRIC_* call-site macros for adding new metrics --- CONTRIBUTING.md | 8 ++++ docs/telemetry-runbook.md | 85 +++++++++++++++++++++++++++++---------- 2 files changed, 72 insertions(+), 21 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 31702e7c3c..d15548e239 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -414,6 +414,14 @@ python .github/scripts/otel-naming/check_otel_naming.py See [.github/scripts/otel-naming/README.md](.github/scripts/otel-naming/README.md) for the full rule list. +## Adding a new OTel metric + +See `src/xrpld/telemetry/MetricMacros.h` for the call-site macros covering every +OTel instrument kind (Counter, UpDownCounter, Histogram, Gauge, and their +Observable/async counterparts) and the "Adding a New Metric" section in +[docs/telemetry-runbook.md](docs/telemetry-runbook.md) for the walkthrough and a +need-to-macro lookup table. + ## Contracts and instrumentation We are using [Antithesis](https://antithesis.com/) for continuous fuzzing, diff --git a/docs/telemetry-runbook.md b/docs/telemetry-runbook.md index c8d602dae1..784390d297 100644 --- a/docs/telemetry-runbook.md +++ b/docs/telemetry-runbook.md @@ -603,27 +603,28 @@ The `OTelCollector` implementation exports metrics via OTLP/HTTP to the same OTe These gauges are exported via the OTel Metrics SDK `PeriodicMetricReader` (10s interval), NOT through beast::insight. -| Prometheus Metric | Source | Description | -| --------------------------------------------------- | ------------------- | -------------------------------------------- | -| `server_info{metric="server_state"}` | MetricsRegistry.cpp | Operating mode (0=DISCONNECTED .. 4=FULL) | -| `server_info{metric="uptime"}` | MetricsRegistry.cpp | Seconds since server start | -| `server_info{metric="peers"}` | MetricsRegistry.cpp | Total connected peers | -| `server_info{metric="validated_ledger_seq"}` | MetricsRegistry.cpp | Validated ledger sequence number | -| `server_info{metric="ledger_current_index"}` | MetricsRegistry.cpp | Current open ledger sequence | -| `server_info{metric="peer_disconnects_resources"}` | MetricsRegistry.cpp | Cumulative resource-related peer disconnects | -| `server_info{metric="last_close_proposers"}` | MetricsRegistry.cpp | Proposers in last closed round | -| `server_info{metric="last_close_converge_time_ms"}` | MetricsRegistry.cpp | Last close convergence time (ms) | -| `build_info{version=""}` | MetricsRegistry.cpp | Info-style metric (always 1) | -| `complete_ledgers{bound="start\|end",index=""}` | MetricsRegistry.cpp | Complete ledger range start/end pairs | -| `db_metrics{metric="db_kb_total"}` | MetricsRegistry.cpp | Total database size (KB) | -| `db_metrics{metric="db_kb_ledger"}` | MetricsRegistry.cpp | Ledger database size (KB) | -| `db_metrics{metric="db_kb_transaction"}` | MetricsRegistry.cpp | Transaction database size (KB) | -| `db_metrics{metric="historical_perminute"}` | MetricsRegistry.cpp | Historical ledger fetches per minute | -| `cache_metrics{metric="AL_size"}` | MetricsRegistry.cpp | AcceptedLedger cache size | -| `nodestore_state{metric="node_reads_duration_us"}` | MetricsRegistry.cpp | Cumulative read time (microseconds) | -| `nodestore_state{metric="read_request_bundle"}` | MetricsRegistry.cpp | Read request bundle count | -| `nodestore_state{metric="read_threads_running"}` | MetricsRegistry.cpp | Active read threads | -| `nodestore_state{metric="read_threads_total"}` | MetricsRegistry.cpp | Total read threads configured | +| Prometheus Metric | Source | Description | +| --------------------------------------------------- | ------------------- | ------------------------------------------------ | +| `server_info{metric="server_state"}` | MetricsRegistry.cpp | Operating mode (0=DISCONNECTED .. 4=FULL) | +| `server_info{metric="uptime"}` | MetricsRegistry.cpp | Seconds since server start | +| `server_info{metric="peers"}` | MetricsRegistry.cpp | Total connected peers | +| `server_info{metric="validated_ledger_seq"}` | MetricsRegistry.cpp | Validated ledger sequence number | +| `server_info{metric="ledger_current_index"}` | MetricsRegistry.cpp | Current open ledger sequence | +| `server_info{metric="peer_disconnects_resources"}` | MetricsRegistry.cpp | Cumulative resource-related peer disconnects | +| `server_info{metric="last_close_proposers"}` | MetricsRegistry.cpp | Proposers in last closed round | +| `server_info{metric="last_close_converge_time_ms"}` | MetricsRegistry.cpp | Last close convergence time (ms) | +| `build_info{version=""}` | MetricsRegistry.cpp | Info-style metric (always 1) | +| `complete_ledgers{bound="start\|end",index=""}` | MetricsRegistry.cpp | Complete ledger range start/end pairs | +| `db_metrics{metric="db_kb_total"}` | MetricsRegistry.cpp | Total database size (KB) | +| `db_metrics{metric="db_kb_ledger"}` | MetricsRegistry.cpp | Ledger database size (KB) | +| `db_metrics{metric="db_kb_transaction"}` | MetricsRegistry.cpp | Transaction database size (KB) | +| `db_metrics{metric="historical_perminute"}` | MetricsRegistry.cpp | Historical ledger fetches per minute | +| `cache_metrics{metric="AL_size"}` | MetricsRegistry.cpp | AcceptedLedger cache size | +| `nodestore_state{metric="node_reads_duration_us"}` | MetricsRegistry.cpp | Cumulative read time (microseconds) | +| `nodestore_state{metric="read_request_bundle"}` | MetricsRegistry.cpp | Read request bundle count | +| `nodestore_state{metric="read_threads_running"}` | MetricsRegistry.cpp | Active read threads | +| `nodestore_state{metric="read_threads_total"}` | MetricsRegistry.cpp | Total read threads configured | +| `rpc_in_flight_requests` | PerfLogImp.cpp | RPC requests currently executing (UpDownCounter) | #### Counters @@ -645,6 +646,48 @@ These gauges are exported via the OTel Metrics SDK `PeriodicMetricReader` (10s i | `pathfind_fast` | PathRequests.h:23 | Fast pathfinding duration (ms) | | `pathfind_full` | PathRequests.h:24 | Full pathfinding duration (ms) | +#### Adding a New Metric + + + + +Use the call-site macros in `src/xrpld/telemetry/MetricMacros.h` -- no +`MetricsRegistry.h`/`.cpp` edit is needed for any of these: + +| Need | Macro | +| ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Monotonic tally (never decreases) | `XRPL_METRIC_COUNTER_INC` / `_ADD` [+ `_LABELED`] | +| Running total that can decrease | `XRPL_METRIC_UPDOWN_ADD` [+ `_LABELED`] | +| Distribution of values (latency, size) | `XRPL_METRIC_HISTOGRAM_RECORD` [+ `_LABELED`] | +| Last-value snapshot (not a distribution) | `XRPL_METRIC_GAUGE_RECORD` [+ `_LABELED`] -- requires an ABI v2 opentelemetry-cpp build; this repo currently builds ABI v1, so use the observable-gauge row below instead | +| Value your own code already tracks, sampled on a timer | `XRPL_METRIC_OBSERVABLE_GAUGE_REGISTER` / `_COUNTER_REGISTER` / `_UPDOWN_REGISTER` | + +```cpp +#include + +// Monotonic counter: +XRPL_METRIC_COUNTER_INC(app_, "my_new_thing_total", "Description of what this counts"); + +// Value that can go up and down, e.g. in-flight work: +XRPL_METRIC_UPDOWN_ADD(app_, "my_in_flight_total", "Currently executing", 1); // on start +XRPL_METRIC_UPDOWN_ADD(app_, "my_in_flight_total", "Currently executing", -1); // on finish + +// Sampled from your own state, on the OTel export timer (register ONCE, in init code): +XRPL_METRIC_OBSERVABLE_GAUGE_REGISTER(app_, "my_thing_size", "Current size", + [this] { return static_cast(myThing_.size()); }); +``` + +Counters use a `_total` suffix by convention. A histogram whose values can +exceed ~10,000 units (e.g. a microsecond duration beyond 10ms) still needs one +line added to `addMicrosecondHistogramView()` in `MetricsRegistry.cpp` -- the +only case that still touches a central file. There is no way to read a metric's +current value back from application code -- OTel's API is write-only by design; +keep your own state if your logic needs to both record and read a running value +(see the Doxygen header in `MetricMacros.h` and "Use Case 4" in +`tasks/metric-macro-plan.md` for the full explanation and the `prometheus-cpp` +contrast rationale). + ## Deployment Tiers Multiple xrpld instances can send telemetry to per-tier collectors that all