cat_gateway/metrics/
memory.rs

1//! Metrics related to memory analytics.
2
3use std::{
4    alloc::System,
5    sync::atomic::{AtomicBool, Ordering},
6    thread,
7};
8
9use memory_stats::{memory_stats, MemoryStats};
10use stats_alloc::{Region, StatsAlloc, INSTRUMENTED_SYSTEM};
11
12use crate::settings::Settings;
13
14/// Use the instrumented allocator for gathering allocation statistics.
15/// Note: This wraps the global allocator.
16/// All structs that use the global allocator can be tracked.
17#[global_allocator]
18static GLOBAL: &StatsAlloc<System> = &INSTRUMENTED_SYSTEM;
19
20/// This is to prevent the init function from accidentally being called multiple times.
21static IS_INITIALIZED: AtomicBool = AtomicBool::new(false);
22
23/// Starts a background thread to periodically update memory metrics.
24///
25/// This function spawns a thread that updates the memory metrics
26/// at regular intervals defined by `METRICS_MEMORY_INTERVAL`.
27pub(crate) fn init_metrics_reporter() {
28    if IS_INITIALIZED.swap(true, Ordering::SeqCst) {
29        return;
30    }
31
32    let stats = Region::new(GLOBAL);
33    let api_host_names = Settings::api_host_names().join(",");
34    let service_id = Settings::service_id();
35
36    thread::spawn(move || {
37        loop {
38            {
39                let allocator_stats = stats.change();
40                let mem_stats = memory_stats().unwrap_or({
41                    MemoryStats {
42                        physical_mem: 0,
43                        virtual_mem: 0,
44                    }
45                });
46
47                reporter::MEMORY_PHYSICAL_USAGE
48                    .with_label_values(&[&api_host_names, service_id])
49                    .set(i64::try_from(mem_stats.physical_mem).unwrap_or(-1));
50                reporter::MEMORY_VIRTUAL_USAGE
51                    .with_label_values(&[&api_host_names, service_id])
52                    .set(i64::try_from(mem_stats.virtual_mem).unwrap_or(-1));
53                reporter::MEMORY_ALLOCATION_COUNT
54                    .with_label_values(&[&api_host_names, service_id])
55                    .set(i64::try_from(allocator_stats.allocations).unwrap_or(-1));
56                reporter::MEMORY_DEALLOCATION_COUNT
57                    .with_label_values(&[&api_host_names, service_id])
58                    .set(i64::try_from(allocator_stats.deallocations).unwrap_or(-1));
59                reporter::MEMORY_REALLOCATION_COUNT
60                    .with_label_values(&[&api_host_names, service_id])
61                    .set(i64::try_from(allocator_stats.reallocations).unwrap_or(-1));
62                reporter::MEMORY_BYTES_ALLOCATED
63                    .with_label_values(&[&api_host_names, service_id])
64                    .set(i64::try_from(allocator_stats.bytes_allocated).unwrap_or(-1));
65                reporter::MEMORY_BYTES_DEALLOCATED
66                    .with_label_values(&[&api_host_names, service_id])
67                    .set(i64::try_from(allocator_stats.bytes_deallocated).unwrap_or(-1));
68                reporter::MEMORY_BYTES_REALLOCATED
69                    .with_label_values(&[&api_host_names, service_id])
70                    .set(i64::try_from(allocator_stats.bytes_reallocated).unwrap_or(-1));
71            }
72
73            thread::sleep(Settings::metrics_memory_interval());
74        }
75    });
76}
77
78/// All the related memory reporting metrics to the Prometheus service are inside this
79/// module.
80mod reporter {
81    use std::sync::LazyLock;
82
83    use prometheus::{register_int_gauge_vec, IntGaugeVec};
84
85    /// Labels for the memory metrics
86    const MEMORY_METRIC_LABELS: [&str; 2] = ["api_host_names", "service_id"];
87
88    /// The "physical" memory used by this process, in bytes.
89    pub(super) static MEMORY_PHYSICAL_USAGE: LazyLock<IntGaugeVec> = LazyLock::new(|| {
90        register_int_gauge_vec!(
91            "memory_physical_usage",
92            "Amount of physical memory usage in bytes",
93            &MEMORY_METRIC_LABELS
94        )
95        .unwrap()
96    });
97
98    /// The "virtual" memory used by this process, in bytes.
99    pub(super) static MEMORY_VIRTUAL_USAGE: LazyLock<IntGaugeVec> = LazyLock::new(|| {
100        register_int_gauge_vec!(
101            "memory_virtual_usage",
102            "Amount of physical virtual usage in bytes",
103            &MEMORY_METRIC_LABELS
104        )
105        .unwrap()
106    });
107
108    /// The number of allocation count in the heap.
109    pub(super) static MEMORY_ALLOCATION_COUNT: LazyLock<IntGaugeVec> = LazyLock::new(|| {
110        register_int_gauge_vec!(
111            "memory_allocation_count",
112            "Number of allocation count in the heap",
113            &MEMORY_METRIC_LABELS
114        )
115        .unwrap()
116    });
117
118    /// The number of deallocation count in the heap.
119    pub(super) static MEMORY_DEALLOCATION_COUNT: LazyLock<IntGaugeVec> = LazyLock::new(|| {
120        register_int_gauge_vec!(
121            "memory_deallocation_count",
122            "Number of deallocation count in the heap",
123            &MEMORY_METRIC_LABELS
124        )
125        .unwrap()
126    });
127
128    /// The number of reallocation count in the heap.
129    pub(super) static MEMORY_REALLOCATION_COUNT: LazyLock<IntGaugeVec> = LazyLock::new(|| {
130        register_int_gauge_vec!(
131            "memory_reallocation_count",
132            "Number of reallocation count in the heap",
133            &MEMORY_METRIC_LABELS
134        )
135        .unwrap()
136    });
137
138    /// The amount of accumulative allocated bytes in the heap.
139    pub(super) static MEMORY_BYTES_ALLOCATED: LazyLock<IntGaugeVec> = LazyLock::new(|| {
140        register_int_gauge_vec!(
141            "memory_bytes_allocated",
142            "Amount of accumulative allocated bytes in the heap",
143            &MEMORY_METRIC_LABELS
144        )
145        .unwrap()
146    });
147
148    /// The amount of accumulative deallocated bytes in the heap.
149    pub(super) static MEMORY_BYTES_DEALLOCATED: LazyLock<IntGaugeVec> = LazyLock::new(|| {
150        register_int_gauge_vec!(
151            "memory_bytes_deallocated",
152            "Amount of accumulative deallocated bytes in the heap",
153            &MEMORY_METRIC_LABELS
154        )
155        .unwrap()
156    });
157
158    /// The amount of accumulative reallocated bytes in the heap.
159    pub(super) static MEMORY_BYTES_REALLOCATED: LazyLock<IntGaugeVec> = LazyLock::new(|| {
160        register_int_gauge_vec!(
161            "memory_bytes_reallocated",
162            "Amount of accumulative reallocated bytes in the heap",
163            &MEMORY_METRIC_LABELS
164        )
165        .unwrap()
166    });
167}