1use std::{
2 borrow::Cow,
3 sync::{Arc, LazyLock},
4};
5
6use opentelemetry::{
7 global,
8 metrics::{Gauge, Histogram},
9 Key, KeyValue, StringValue, Value,
10};
11
12pub(crate) static POOL_METRICS: LazyLock<Arc<Metrics>> = LazyLock::new(|| Arc::new(Metrics::new()));
14
15const KEY_POOL_NAME: Key = Key::from_static_str("db.client.connection.pool.name");
16const KEY_STATE: Key = Key::from_static_str("db.client.connection.state");
17
18pub(crate) struct Metrics {
20 pub conn_count: Gauge<u64>,
22 pub wait_time: Histogram<f64>,
24 pub use_time: Histogram<f64>,
26 pub op_duration: Histogram<f64>,
28 pub idle_max: Gauge<u64>,
30 pub idle_min: Gauge<u64>,
32 pub conn_max: Gauge<u64>,
34}
35
36impl Metrics {
37 pub(crate) fn new() -> Self {
41 let meter = global::meter("uxum-pools");
42 let conn_count = meter
46 .u64_gauge("db.client.connection.count")
47 .with_description("The number of connections that are currently in state described by the state attribute.")
48 .build();
49 let wait_time = meter
51 .f64_histogram("db.client.connection.wait_time")
52 .with_unit("s")
53 .with_description("The time it took to obtain an open connection from the pool.")
54 .build();
55 let use_time = meter
57 .f64_histogram("db.client.connection.use_time")
58 .with_unit("s")
59 .with_description(
60 "The time between borrowing a connection and returning it to the pool.",
61 )
62 .build();
63 let op_duration = meter
76 .f64_histogram("db.client.operation.duration")
77 .with_unit("s")
78 .with_description("Duration of database client operations.")
79 .build();
80 let idle_max = meter
82 .u64_gauge("db.client.connection.idle.max")
83 .with_description("The maximum number of idle open connections allowed.")
84 .build();
85 let idle_min = meter
87 .u64_gauge("db.client.connection.idle.min")
88 .with_description("The minimum number of idle open connections allowed.")
89 .build();
90 let conn_max = meter
92 .u64_gauge("db.client.connection.max")
93 .with_description("The maximum number of open connections allowed.")
94 .build();
95 Metrics {
96 conn_count,
97 wait_time,
98 use_time,
99 op_duration,
100 idle_max,
101 idle_min,
102 conn_max,
103 }
104 }
105
106 pub(crate) fn record_state(&self, label: &[KeyValue], state: PoolState) {
107 if let Some(max_size) = state.max_size {
108 self.conn_max.record(max_size as u64, label);
109 }
110 if let Some(size) = state.size {
111 let total_label = status_kv(label[0].clone(), "total");
112 self.conn_count.record(size as u64, &total_label);
113 }
114 if let Some(idle) = state.idle {
115 let idle_label = status_kv(label[0].clone(), "idle");
116 self.conn_count.record(idle as u64, &idle_label);
117 }
118 if let Some(in_use) = state.in_use {
119 let used_label = status_kv(label[0].clone(), "used");
120 self.conn_count.record(in_use as u64, &used_label);
121 }
122 if let Some(min_idle) = state.min_idle {
123 self.idle_min.record(min_idle as u64, label);
124 }
125 if let Some(max_idle) = state.max_idle {
126 self.idle_max.record(max_idle as u64, label);
127 }
128 }
129}
130
131impl Default for Metrics {
132 fn default() -> Self {
133 Metrics::new()
134 }
135}
136
137pub(crate) fn pool_kv(name: Option<Cow<'static, str>>) -> [KeyValue; 1] {
138 match name {
139 Some(n) => [KeyValue::new(KEY_POOL_NAME, n)],
140 None => [KeyValue::new(KEY_POOL_NAME, "default")],
141 }
142}
143
144pub(crate) fn status_kv(name: KeyValue, status: &'static str) -> [KeyValue; 2] {
145 [
146 name,
147 KeyValue::new(KEY_STATE, Value::String(StringValue::from(status))),
148 ]
149}
150
151#[derive(Clone, Debug, Default, PartialEq, Eq)]
155pub struct PoolState {
156 pub max_size: Option<usize>,
158 pub size: Option<usize>,
160 pub idle: Option<usize>,
162 pub in_use: Option<usize>,
164 pub min_idle: Option<usize>,
166 pub max_idle: Option<usize>,
168}