bothan_lib/metrics/
store.rs

1//! Metrics collection for store operations.
2//!
3//! This module provides the [`Metrics`] struct and related types for tracking store operation statistics
4//! such as the number of operations and their execution durations. It leverages OpenTelemetry for metrics
5//! instrumentation, supporting monitoring and observability.
6
7use opentelemetry::metrics::{Counter, Histogram};
8use opentelemetry::{KeyValue, global};
9use strum_macros::Display;
10
11/// Holds counters and histograms for store operation metrics.
12#[derive(Clone, Debug)]
13pub struct Metrics {
14    /// Counter tracking total store operations.
15    operations_total: Counter<u64>,
16
17    /// Histogram recording operation durations in microseconds.
18    operation_duration: Histogram<u64>,
19}
20
21impl Default for Metrics {
22    fn default() -> Self {
23        Self::new()
24    }
25}
26
27impl Metrics {
28    /// Creates a new [`Metrics`] instance configured for the store.
29    ///
30    /// # Examples
31    ///
32    /// ```rust
33    /// use bothan_lib::metrics::store::{Metrics, Operation, OperationStatus};
34    ///
35    /// let metrics = Metrics::new();
36    /// metrics.update_store_operation("source".to_string(), 100, Operation::GetAssetInfo, OperationStatus::Success);
37    /// ```
38    pub fn new() -> Self {
39        let meter = global::meter("store");
40
41        let operations_total = meter
42            .u64_counter("store_operations")
43            .with_description("Total number of operations executed by the store")
44            .build();
45        let operation_duration = meter
46            .u64_histogram("store_operation_duration_microseconds")
47            .with_description("Time taken to execute each store operation")
48            .with_unit("microseconds")
49            .build();
50
51        Self {
52            operations_total,
53            operation_duration,
54        }
55    }
56
57    /// Records a store operation result and duration.
58    ///
59    /// # Arguments
60    ///
61    /// * `source` - The source of the operation.
62    /// * `elapsed_time` - Duration of the operation in microseconds.
63    /// * `operation` - The type of operation performed.
64    /// * `status` - The result of the operation.
65    pub fn update_store_operation(
66        &self,
67        source: String,
68        elapsed_time: u128,
69        operation: Operation,
70        status: OperationStatus,
71    ) {
72        let labels = &[
73            KeyValue::new("source", source),
74            KeyValue::new("operation", operation.to_string()),
75            KeyValue::new("status", status.to_string()),
76        ];
77        self.operations_total.add(1, labels);
78        // `elapsed_time` is u128, but it will never exceed u64::MAX in practice
79        self.operation_duration.record(elapsed_time as u64, labels);
80    }
81}
82
83/// Possible store operations.
84#[derive(Display)]
85#[strum(serialize_all = "snake_case")]
86pub enum Operation {
87    /// Retrieve asset information from the store.
88    GetAssetInfo,
89    /// Insert a batch of asset information into the store.
90    InsertBatchAssetInfo,
91}
92
93/// Possible results for a store operation.
94#[derive(Display)]
95#[strum(serialize_all = "snake_case")]
96pub enum OperationStatus {
97    /// The operation completed successfully.
98    Success,
99    /// The operation completed but the data was stale.
100    Stale,
101    /// The requested data was not found.
102    NotFound,
103    /// The operation failed.
104    Failed,
105}