Skip to main content

ipfrs_cli/commands/
metrics.rs

1//! Prometheus metrics commands
2//!
3//! Provides the `ipfrs metrics` subcommand family:
4//!
5//! - `ipfrs metrics show`  — Print all node metrics in Prometheus text format.
6//! - `ipfrs metrics reset` — Reset all per-node counters to zero (no-op for the
7//!   global registry; instructs the user to restart the daemon for a full reset).
8
9use anyhow::Result;
10
11use crate::commands::query::OutputFormat;
12use crate::output::{self, print_header};
13
14/// Handle `ipfrs metrics show`.
15///
16/// Fetches the Prometheus text output from the `ipfrs-interface` global
17/// registry and prints it to stdout.
18///
19/// When `format` is [`OutputFormat::Json`], a minimal JSON envelope is emitted
20/// instead of raw Prometheus text so the output can be consumed by tools that
21/// expect JSON.
22pub async fn handle_metrics_show(format: &OutputFormat) -> Result<()> {
23    let text = ipfrs_interface::metrics::encode_metrics()
24        .map_err(|e| anyhow::anyhow!("Failed to encode metrics: {}", e))?;
25
26    if format.is_json() {
27        // Wrap in a simple JSON object so callers can parse it uniformly.
28        println!("{{");
29        println!("  \"format\": \"prometheus_text\",");
30        // Escape the inner text for JSON embedding.
31        let escaped = text
32            .replace('\\', "\\\\")
33            .replace('"', "\\\"")
34            .replace('\n', "\\n");
35        println!("  \"metrics\": \"{}\"", escaped);
36        println!("}}");
37    } else {
38        print_header("IPFRS Prometheus Metrics");
39        print!("{}", text);
40    }
41
42    Ok(())
43}
44
45/// Handle `ipfrs metrics reset`.
46///
47/// Prometheus counters are monotonically increasing and cannot be reset at
48/// runtime without a process restart.  This command prints an informative
49/// message and exits successfully so automation pipelines are not broken.
50pub async fn handle_metrics_reset() -> Result<()> {
51    output::info(
52        "Prometheus counters are monotonically increasing and cannot be reset at runtime.\n\
53         To reset all metrics, restart the IPFRS daemon process.",
54    );
55    Ok(())
56}
57
58#[cfg(test)]
59mod tests {
60    use super::*;
61
62    #[tokio::test]
63    async fn test_metrics_show_text() {
64        // Should not return an error even when no metrics have been recorded.
65        let result = handle_metrics_show(&OutputFormat::Text).await;
66        assert!(result.is_ok(), "metrics show (text) failed: {:?}", result);
67    }
68
69    #[tokio::test]
70    async fn test_metrics_show_json() {
71        let result = handle_metrics_show(&OutputFormat::Json).await;
72        assert!(result.is_ok(), "metrics show (json) failed: {:?}", result);
73    }
74
75    #[tokio::test]
76    async fn test_metrics_reset() {
77        let result = handle_metrics_reset().await;
78        assert!(result.is_ok(), "metrics reset failed: {:?}", result);
79    }
80}