cdk_prometheus/
metrics.rs

1use std::sync::Arc;
2
3use prometheus::{
4    Histogram, HistogramVec, IntCounter, IntCounterVec, IntGauge, IntGaugeVec, Registry,
5};
6
7/// Global metrics instance
8pub static METRICS: std::sync::LazyLock<CdkMetrics> = std::sync::LazyLock::new(CdkMetrics::default);
9
10/// Custom metrics for CDK applications
11#[derive(Clone, Debug)]
12pub struct CdkMetrics {
13    registry: Arc<Registry>,
14
15    // HTTP metrics
16    http_requests_total: IntCounterVec,
17    http_request_duration: HistogramVec,
18
19    // Authentication metrics
20    auth_attempts_total: IntCounter,
21    auth_successes_total: IntCounter,
22
23    // Lightning metrics
24    lightning_payments_total: IntCounter,
25    lightning_payment_amount: Histogram,
26    lightning_payment_fees: Histogram,
27
28    // Database metrics
29    db_operations_total: IntCounter,
30    db_operation_duration: HistogramVec,
31    db_connections_active: IntGauge,
32
33    // Error metrics
34    errors_total: IntCounter,
35
36    // Mint metrics
37    mint_operations_total: IntCounterVec,
38    mint_in_flight_requests: IntGaugeVec,
39    mint_operation_duration: HistogramVec,
40}
41
42impl CdkMetrics {
43    /// Create a new instance with default metrics
44    ///
45    /// # Errors
46    /// Returns an error if any of the metrics cannot be created or registered
47    pub fn new() -> crate::Result<Self> {
48        let registry = Arc::new(Registry::new());
49
50        // Create and register HTTP metrics
51        let (http_requests_total, http_request_duration) = Self::create_http_metrics(&registry)?;
52
53        // Create and register authentication metrics
54        let (auth_attempts_total, auth_successes_total) = Self::create_auth_metrics(&registry)?;
55
56        // Create and register Lightning metrics
57        let (lightning_payments_total, lightning_payment_amount, lightning_payment_fees) =
58            Self::create_lightning_metrics(&registry)?;
59
60        // Create and register database metrics
61        let (db_operations_total, db_operation_duration, db_connections_active) =
62            Self::create_db_metrics(&registry)?;
63
64        // Create and register error metrics
65        let errors_total = Self::create_error_metrics(&registry)?;
66
67        // Create and register mint metrics
68        let (mint_operations_total, mint_operation_duration, mint_in_flight_requests) =
69            Self::create_mint_metrics(&registry)?;
70
71        Ok(Self {
72            registry,
73            http_requests_total,
74            http_request_duration,
75            auth_attempts_total,
76            auth_successes_total,
77            lightning_payments_total,
78            lightning_payment_amount,
79            lightning_payment_fees,
80            db_operations_total,
81            db_operation_duration,
82            db_connections_active,
83            errors_total,
84            mint_operations_total,
85            mint_in_flight_requests,
86            mint_operation_duration,
87        })
88    }
89
90    /// Create and register HTTP metrics
91    ///
92    /// # Errors
93    /// Returns an error if any of the metrics cannot be created or registered
94    fn create_http_metrics(registry: &Registry) -> crate::Result<(IntCounterVec, HistogramVec)> {
95        let http_requests_total = IntCounterVec::new(
96            prometheus::Opts::new("cdk_http_requests_total", "Total number of HTTP requests"),
97            &["endpoint", "status"],
98        )?;
99        registry.register(Box::new(http_requests_total.clone()))?;
100
101        let http_request_duration = HistogramVec::new(
102            prometheus::HistogramOpts::new(
103                "cdk_http_request_duration_seconds",
104                "HTTP request duration in seconds",
105            )
106            .buckets(vec![
107                0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0,
108            ]),
109            &["endpoint"],
110        )?;
111        registry.register(Box::new(http_request_duration.clone()))?;
112
113        Ok((http_requests_total, http_request_duration))
114    }
115
116    /// Create and register authentication metrics
117    ///
118    /// # Errors
119    /// Returns an error if any of the metrics cannot be created or registered
120    fn create_auth_metrics(registry: &Registry) -> crate::Result<(IntCounter, IntCounter)> {
121        let auth_attempts_total =
122            IntCounter::new("cdk_auth_attempts_total", "Total authentication attempts")?;
123        registry.register(Box::new(auth_attempts_total.clone()))?;
124
125        let auth_successes_total = IntCounter::new(
126            "cdk_auth_successes_total",
127            "Total successful authentications",
128        )?;
129        registry.register(Box::new(auth_successes_total.clone()))?;
130
131        Ok((auth_attempts_total, auth_successes_total))
132    }
133
134    /// Create and register Lightning metrics
135    ///
136    /// # Errors
137    /// Returns an error if any of the metrics cannot be created or registered
138    fn create_lightning_metrics(
139        registry: &Registry,
140    ) -> crate::Result<(IntCounter, Histogram, Histogram)> {
141        let wallet_operations_total =
142            IntCounter::new("cdk_wallet_operations_total", "Total wallet operations")?;
143        registry.register(Box::new(wallet_operations_total))?;
144
145        let lightning_payments_total =
146            IntCounter::new("cdk_lightning_payments_total", "Total Lightning payments")?;
147        registry.register(Box::new(lightning_payments_total.clone()))?;
148
149        let lightning_payment_amount = Histogram::with_opts(
150            prometheus::HistogramOpts::new(
151                "cdk_lightning_payment_amount_sats",
152                "Lightning payment amounts in satoshis",
153            )
154            .buckets(vec![
155                1.0,
156                10.0,
157                100.0,
158                1000.0,
159                10_000.0,
160                100_000.0,
161                1_000_000.0,
162            ]),
163        )?;
164        registry.register(Box::new(lightning_payment_amount.clone()))?;
165
166        let lightning_payment_fees = Histogram::with_opts(
167            prometheus::HistogramOpts::new(
168                "cdk_lightning_payment_fees_sats",
169                "Lightning payment fees in satoshis",
170            )
171            .buckets(vec![0.0, 1.0, 5.0, 10.0, 50.0, 100.0, 500.0, 1000.0]),
172        )?;
173        registry.register(Box::new(lightning_payment_fees.clone()))?;
174
175        Ok((
176            lightning_payments_total,
177            lightning_payment_amount,
178            lightning_payment_fees,
179        ))
180    }
181
182    /// Create and register database metrics
183    ///
184    /// # Errors
185    /// Returns an error if any of the metrics cannot be created or registered
186    fn create_db_metrics(
187        registry: &Registry,
188    ) -> crate::Result<(IntCounter, HistogramVec, IntGauge)> {
189        let db_operations_total =
190            IntCounter::new("cdk_db_operations_total", "Total database operations")?;
191        registry.register(Box::new(db_operations_total.clone()))?;
192        let db_operation_duration = HistogramVec::new(
193            prometheus::HistogramOpts::new(
194                "cdk_db_operation_duration_seconds",
195                "Database operation duration in seconds",
196            )
197            .buckets(vec![0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0]),
198            &["operation"],
199        )?;
200        registry.register(Box::new(db_operation_duration.clone()))?;
201
202        let db_connections_active = IntGauge::new(
203            "cdk_db_connections_active",
204            "Number of active database connections",
205        )?;
206        registry.register(Box::new(db_connections_active.clone()))?;
207
208        Ok((
209            db_operations_total,
210            db_operation_duration,
211            db_connections_active,
212        ))
213    }
214
215    /// Create and register error metrics
216    ///
217    /// # Errors
218    /// Returns an error if any of the metrics cannot be created or registered
219    fn create_error_metrics(registry: &Registry) -> crate::Result<IntCounter> {
220        let errors_total = IntCounter::new("cdk_errors_total", "Total errors")?;
221        registry.register(Box::new(errors_total.clone()))?;
222
223        Ok(errors_total)
224    }
225
226    /// Create and register mint metrics
227    ///
228    /// # Errors
229    /// Returns an error if any of the metrics cannot be created or registered
230    fn create_mint_metrics(
231        registry: &Registry,
232    ) -> crate::Result<(IntCounterVec, HistogramVec, IntGaugeVec)> {
233        let mint_operations_total = IntCounterVec::new(
234            prometheus::Opts::new(
235                "cdk_mint_operations_total",
236                "Total number of mint operations",
237            ),
238            &["operation", "status"],
239        )?;
240        registry.register(Box::new(mint_operations_total.clone()))?;
241
242        let mint_operation_duration = HistogramVec::new(
243            prometheus::HistogramOpts::new(
244                "cdk_mint_operation_duration_seconds",
245                "Duration of mint operations in seconds",
246            )
247            .buckets(vec![
248                0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0,
249            ]),
250            &["operation", "status"],
251        )?;
252        registry.register(Box::new(mint_operation_duration.clone()))?;
253
254        let mint_in_flight_requests = IntGaugeVec::new(
255            prometheus::Opts::new(
256                "cdk_mint_in_flight_requests",
257                "Number of in-flight mint requests",
258            ),
259            &["operation"],
260        )?;
261        registry.register(Box::new(mint_in_flight_requests.clone()))?;
262
263        Ok((
264            mint_operations_total,
265            mint_operation_duration,
266            mint_in_flight_requests,
267        ))
268    }
269
270    /// Get the metrics registry
271    #[must_use]
272    pub fn registry(&self) -> Arc<Registry> {
273        Arc::<Registry>::clone(&self.registry)
274    }
275
276    // HTTP metrics methods
277    pub fn record_http_request(&self, endpoint: &str, status: &str) {
278        self.http_requests_total
279            .with_label_values(&[endpoint, status])
280            .inc();
281    }
282
283    pub fn record_http_request_duration(&self, duration_seconds: f64, endpoint: &str) {
284        self.http_request_duration
285            .with_label_values(&[endpoint])
286            .observe(duration_seconds);
287    }
288
289    // Authentication metrics methods
290    pub fn record_auth_attempt(&self) {
291        self.auth_attempts_total.inc();
292    }
293
294    pub fn record_auth_success(&self) {
295        self.auth_successes_total.inc();
296    }
297
298    // Lightning metrics methods
299    pub fn record_lightning_payment(&self, amount: f64, fee: f64) {
300        self.lightning_payments_total.inc();
301        self.lightning_payment_amount.observe(amount);
302        self.lightning_payment_fees.observe(fee);
303    }
304
305    // Database metrics methods
306    pub fn record_db_operation(&self, duration_seconds: f64, op: &str) {
307        self.db_operations_total.inc();
308        self.db_operation_duration
309            .with_label_values(&[op])
310            .observe(duration_seconds);
311    }
312
313    pub fn set_db_connections_active(&self, count: i64) {
314        self.db_connections_active.set(count);
315    }
316
317    // Error metrics methods
318    pub fn record_error(&self) {
319        self.errors_total.inc();
320    }
321
322    // Mint metrics methods
323    pub fn record_mint_operation(&self, operation: &str, success: bool) {
324        let status = if success { "success" } else { "error" };
325        self.mint_operations_total
326            .with_label_values(&[operation, status])
327            .inc();
328    }
329    pub fn record_mint_operation_histogram(
330        &self,
331        operation: &str,
332        success: bool,
333        duration_seconds: f64,
334    ) {
335        let status = if success { "success" } else { "error" };
336        self.mint_operation_duration
337            .with_label_values(&[operation, status])
338            .observe(duration_seconds);
339    }
340    pub fn inc_in_flight_requests(&self, operation: &str) {
341        self.mint_in_flight_requests
342            .with_label_values(&[operation])
343            .inc();
344    }
345
346    pub fn dec_in_flight_requests(&self, operation: &str) {
347        self.mint_in_flight_requests
348            .with_label_values(&[operation])
349            .dec();
350    }
351}
352
353impl Default for CdkMetrics {
354    fn default() -> Self {
355        Self::new().expect("Failed to create default CdkMetrics")
356    }
357}
358
359/// Helper functions for recording metrics using the global instance
360pub mod global {
361    use super::METRICS;
362
363    /// Record an HTTP request using the global metrics instance
364    pub fn record_http_request(endpoint: &str, status: &str) {
365        METRICS.record_http_request(endpoint, status);
366    }
367
368    /// Record HTTP request duration using the global metrics instance
369    pub fn record_http_request_duration(duration_seconds: f64, endpoint: &str) {
370        METRICS.record_http_request_duration(duration_seconds, endpoint);
371    }
372
373    /// Record authentication attempt using the global metrics instance
374    pub fn record_auth_attempt() {
375        METRICS.record_auth_attempt();
376    }
377
378    /// Record authentication success using the global metrics instance
379    pub fn record_auth_success() {
380        METRICS.record_auth_success();
381    }
382
383    /// Record Lightning payment using the global metrics instance
384    pub fn record_lightning_payment(amount: f64, fee: f64) {
385        METRICS.record_lightning_payment(amount, fee);
386    }
387
388    /// Record database operation using the global metrics instance
389    pub fn record_db_operation(duration_seconds: f64, op: &str) {
390        METRICS.record_db_operation(duration_seconds, op);
391    }
392
393    /// Set database connections active using the global metrics instance
394    pub fn set_db_connections_active(count: i64) {
395        METRICS.set_db_connections_active(count);
396    }
397
398    /// Record error using the global metrics instance
399    pub fn record_error() {
400        METRICS.record_error();
401    }
402
403    /// Record mint operation using the global metrics instance
404    pub fn record_mint_operation(operation: &str, success: bool) {
405        METRICS.record_mint_operation(operation, success);
406    }
407
408    /// Record mint operation with histogram using the global metrics instance
409    pub fn record_mint_operation_histogram(operation: &str, success: bool, duration_seconds: f64) {
410        METRICS.record_mint_operation_histogram(operation, success, duration_seconds);
411    }
412
413    /// Increment in-flight requests using the global metrics instance
414    pub fn inc_in_flight_requests(operation: &str) {
415        METRICS.inc_in_flight_requests(operation);
416    }
417
418    /// Decrement in-flight requests using the global metrics instance
419    pub fn dec_in_flight_requests(operation: &str) {
420        METRICS.dec_in_flight_requests(operation);
421    }
422
423    /// Get the metrics registry from the global instance
424    pub fn registry() -> std::sync::Arc<prometheus::Registry> {
425        METRICS.registry()
426    }
427}