Skip to content

Commit c58f955

Browse files
authored
Add more metrics to prometheus endpoint (#263)
This change: - Adds server metrics to prometheus endpoint. - Adds database metrics to prometheus endpoint. - Adds pools metrics to prometheus endpoint. - Change metrics name to have a prefix of (stats|pools|databases|servers).
1 parent ca89019 commit c58f955

File tree

1 file changed

+220
-58
lines changed

1 file changed

+220
-58
lines changed

src/prometheus.rs

Lines changed: 220 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use std::net::SocketAddr;
88

99
use crate::config::Address;
1010
use crate::pool::get_all_pools;
11-
use crate::stats::get_address_stats;
11+
use crate::stats::{get_address_stats, get_pool_stats, get_server_stats, ServerInformation};
1212

1313
struct MetricHelpType {
1414
help: &'static str,
@@ -19,113 +19,141 @@ struct MetricHelpType {
1919
// counters only increase
2020
// gauges can arbitrarily increase or decrease
2121
static METRIC_HELP_AND_TYPES_LOOKUP: phf::Map<&'static str, MetricHelpType> = phf_map! {
22-
"total_query_count" => MetricHelpType {
22+
"stats_total_query_count" => MetricHelpType {
2323
help: "Number of queries sent by all clients",
2424
ty: "counter",
2525
},
26-
"total_query_time" => MetricHelpType {
26+
"stats_total_query_time" => MetricHelpType {
2727
help: "Total amount of time for queries to execute",
2828
ty: "counter",
2929
},
30-
"total_received" => MetricHelpType {
30+
"stats_total_received" => MetricHelpType {
3131
help: "Number of bytes received from the server",
3232
ty: "counter",
3333
},
34-
"total_sent" => MetricHelpType {
34+
"stats_total_sent" => MetricHelpType {
3535
help: "Number of bytes sent to the server",
3636
ty: "counter",
3737
},
38-
"total_xact_count" => MetricHelpType {
38+
"stats_total_xact_count" => MetricHelpType {
3939
help: "Total number of transactions started by the client",
4040
ty: "counter",
4141
},
42-
"total_xact_time" => MetricHelpType {
42+
"stats_total_xact_time" => MetricHelpType {
4343
help: "Total amount of time for all transactions to execute",
4444
ty: "counter",
4545
},
46-
"total_wait_time" => MetricHelpType {
46+
"stats_total_wait_time" => MetricHelpType {
4747
help: "Total time client waited for a server connection",
4848
ty: "counter",
4949
},
50-
"avg_query_count" => MetricHelpType {
50+
"stats_avg_query_count" => MetricHelpType {
5151
help: "Average of total_query_count every 15 seconds",
5252
ty: "gauge",
5353
},
54-
"avg_query_time" => MetricHelpType {
54+
"stats_avg_query_time" => MetricHelpType {
5555
help: "Average time taken for queries to execute every 15 seconds",
5656
ty: "gauge",
5757
},
58-
"avg_recv" => MetricHelpType {
58+
"stats_avg_recv" => MetricHelpType {
5959
help: "Average of total_received bytes every 15 seconds",
6060
ty: "gauge",
6161
},
62-
"avg_sent" => MetricHelpType {
62+
"stats_avg_sent" => MetricHelpType {
6363
help: "Average of total_sent bytes every 15 seconds",
6464
ty: "gauge",
6565
},
66-
"avg_errors" => MetricHelpType {
66+
"stats_avg_errors" => MetricHelpType {
6767
help: "Average number of errors every 15 seconds",
6868
ty: "gauge",
6969
},
70-
"avg_xact_count" => MetricHelpType {
70+
"stats_avg_xact_count" => MetricHelpType {
7171
help: "Average of total_xact_count every 15 seconds",
7272
ty: "gauge",
7373
},
74-
"avg_xact_time" => MetricHelpType {
74+
"stats_avg_xact_time" => MetricHelpType {
7575
help: "Average of total_xact_time every 15 seconds",
7676
ty: "gauge",
7777
},
78-
"avg_wait_time" => MetricHelpType {
78+
"stats_avg_wait_time" => MetricHelpType {
7979
help: "Average of total_wait_time every 15 seconds",
8080
ty: "gauge",
8181
},
82-
"maxwait_us" => MetricHelpType {
82+
"pools_maxwait_us" => MetricHelpType {
8383
help: "The time a client waited for a server connection in microseconds",
8484
ty: "gauge",
8585
},
86-
"maxwait" => MetricHelpType {
86+
"pools_maxwait" => MetricHelpType {
8787
help: "The time a client waited for a server connection in seconds",
8888
ty: "gauge",
8989
},
90-
"cl_waiting" => MetricHelpType {
90+
"pools_cl_waiting" => MetricHelpType {
9191
help: "How many clients are waiting for a connection from the pool",
9292
ty: "gauge",
9393
},
94-
"cl_active" => MetricHelpType {
94+
"pools_cl_active" => MetricHelpType {
9595
help: "How many clients are actively communicating with a server",
9696
ty: "gauge",
9797
},
98-
"cl_idle" => MetricHelpType {
98+
"pools_cl_idle" => MetricHelpType {
9999
help: "How many clients are idle",
100100
ty: "gauge",
101101
},
102-
"sv_idle" => MetricHelpType {
102+
"pools_sv_idle" => MetricHelpType {
103103
help: "How many server connections are idle",
104104
ty: "gauge",
105105
},
106-
"sv_active" => MetricHelpType {
106+
"pools_sv_active" => MetricHelpType {
107107
help: "How many server connections are actively communicating with a client",
108108
ty: "gauge",
109109
},
110-
"sv_login" => MetricHelpType {
110+
"pools_sv_login" => MetricHelpType {
111111
help: "How many server connections are currently being created",
112112
ty: "gauge",
113113
},
114-
"sv_tested" => MetricHelpType {
114+
"pools_sv_tested" => MetricHelpType {
115115
help: "How many server connections are currently waiting on a health check to succeed",
116116
ty: "gauge",
117117
},
118+
"servers_bytes_received" => MetricHelpType {
119+
help: "Volume in bytes of network traffic received by server",
120+
ty: "gauge",
121+
},
122+
"servers_bytes_sent" => MetricHelpType {
123+
help: "Volume in bytes of network traffic sent by server",
124+
ty: "gauge",
125+
},
126+
"servers_transaction_count" => MetricHelpType {
127+
help: "Number of transactions executed by server",
128+
ty: "gauge",
129+
},
130+
"servers_query_count" => MetricHelpType {
131+
help: "Number of queries executed by server",
132+
ty: "gauge",
133+
},
134+
"servers_error_count" => MetricHelpType {
135+
help: "Number of errors",
136+
ty: "gauge",
137+
},
138+
"databases_pool_size" => MetricHelpType {
139+
help: "Maximum number of server connections",
140+
ty: "gauge",
141+
},
142+
"databases_current_connections" => MetricHelpType {
143+
help: "Current number of connections for this database",
144+
ty: "gauge",
145+
},
118146
};
119147

120-
struct PrometheusMetric {
148+
struct PrometheusMetric<Value: fmt::Display> {
121149
name: String,
122150
help: String,
123151
ty: String,
124152
labels: HashMap<&'static str, String>,
125-
value: i64,
153+
value: Value,
126154
}
127155

128-
impl fmt::Display for PrometheusMetric {
156+
impl<Value: fmt::Display> fmt::Display for PrometheusMetric<Value> {
129157
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
130158
let formatted_labels = self
131159
.labels
@@ -145,50 +173,81 @@ impl fmt::Display for PrometheusMetric {
145173
}
146174
}
147175

148-
impl PrometheusMetric {
149-
fn new(address: &Address, name: &str, value: i64) -> Option<PrometheusMetric> {
150-
let mut labels = HashMap::new();
151-
labels.insert("host", address.host.clone());
152-
labels.insert("shard", address.shard.to_string());
153-
labels.insert("role", address.role.to_string());
154-
labels.insert("database", address.database.to_string());
155-
176+
impl<Value: fmt::Display> PrometheusMetric<Value> {
177+
fn from_name<V: fmt::Display>(
178+
name: &str,
179+
value: V,
180+
labels: HashMap<&'static str, String>,
181+
) -> Option<PrometheusMetric<V>> {
156182
METRIC_HELP_AND_TYPES_LOOKUP
157183
.get(name)
158-
.map(|metric| PrometheusMetric {
184+
.map(|metric| PrometheusMetric::<V> {
159185
name: name.to_owned(),
160186
help: metric.help.to_owned(),
161187
ty: metric.ty.to_owned(),
162-
labels,
163188
value,
189+
labels,
164190
})
165191
}
192+
193+
fn from_database_info(
194+
address: &Address,
195+
name: &str,
196+
value: u32,
197+
) -> Option<PrometheusMetric<u32>> {
198+
let mut labels = HashMap::new();
199+
labels.insert("host", address.host.clone());
200+
labels.insert("shard", address.shard.to_string());
201+
labels.insert("role", address.role.to_string());
202+
labels.insert("pool", address.pool_name.clone());
203+
labels.insert("database", address.database.to_string());
204+
205+
Self::from_name(&format!("databases_{}", name), value, labels)
206+
}
207+
208+
fn from_server_info(
209+
address: &Address,
210+
name: &str,
211+
value: u64,
212+
) -> Option<PrometheusMetric<u64>> {
213+
let mut labels = HashMap::new();
214+
labels.insert("host", address.host.clone());
215+
labels.insert("shard", address.shard.to_string());
216+
labels.insert("role", address.role.to_string());
217+
labels.insert("pool", address.pool_name.clone());
218+
labels.insert("database", address.database.to_string());
219+
220+
Self::from_name(&format!("servers_{}", name), value, labels)
221+
}
222+
223+
fn from_address(address: &Address, name: &str, value: i64) -> Option<PrometheusMetric<i64>> {
224+
let mut labels = HashMap::new();
225+
labels.insert("host", address.host.clone());
226+
labels.insert("shard", address.shard.to_string());
227+
labels.insert("pool", address.pool_name.clone());
228+
labels.insert("role", address.role.to_string());
229+
labels.insert("database", address.database.to_string());
230+
231+
Self::from_name(&format!("stats_{}", name), value, labels)
232+
}
233+
234+
fn from_pool(pool: &(String, String), name: &str, value: i64) -> Option<PrometheusMetric<i64>> {
235+
let mut labels = HashMap::new();
236+
labels.insert("pool", pool.0.clone());
237+
labels.insert("user", pool.1.clone());
238+
239+
Self::from_name(&format!("pools_{}", name), value, labels)
240+
}
166241
}
167242

168243
async fn prometheus_stats(request: Request<Body>) -> Result<Response<Body>, hyper::http::Error> {
169244
match (request.method(), request.uri().path()) {
170245
(&Method::GET, "/metrics") => {
171-
let stats: HashMap<usize, HashMap<String, i64>> = get_address_stats();
172-
173246
let mut lines = Vec::new();
174-
for (_, pool) in get_all_pools() {
175-
for shard in 0..pool.shards() {
176-
for server in 0..pool.servers(shard) {
177-
let address = pool.address(shard, server);
178-
if let Some(address_stats) = stats.get(&address.id) {
179-
for (key, value) in address_stats.iter() {
180-
if let Some(prometheus_metric) =
181-
PrometheusMetric::new(address, key, *value)
182-
{
183-
lines.push(prometheus_metric.to_string());
184-
} else {
185-
warn!("Metric {} not implemented for {}", key, address.name());
186-
}
187-
}
188-
}
189-
}
190-
}
191-
}
247+
push_address_stats(&mut lines);
248+
push_pool_stats(&mut lines);
249+
push_server_stats(&mut lines);
250+
push_database_stats(&mut lines);
192251

193252
Response::builder()
194253
.header("content-type", "text/plain; version=0.0.4")
@@ -200,6 +259,109 @@ async fn prometheus_stats(request: Request<Body>) -> Result<Response<Body>, hype
200259
}
201260
}
202261

262+
// Adds metrics shown in a SHOW STATS admin command.
263+
fn push_address_stats(lines: &mut Vec<String>) {
264+
let address_stats: HashMap<usize, HashMap<String, i64>> = get_address_stats();
265+
for (_, pool) in get_all_pools() {
266+
for shard in 0..pool.shards() {
267+
for server in 0..pool.servers(shard) {
268+
let address = pool.address(shard, server);
269+
if let Some(address_stats) = address_stats.get(&address.id) {
270+
for (key, value) in address_stats.iter() {
271+
if let Some(prometheus_metric) =
272+
PrometheusMetric::<i64>::from_address(address, key, *value)
273+
{
274+
lines.push(prometheus_metric.to_string());
275+
} else {
276+
warn!("Metric {} not implemented for {}", key, address.name());
277+
}
278+
}
279+
}
280+
}
281+
}
282+
}
283+
}
284+
285+
// Adds relevant metrics shown in a SHOW POOLS admin command.
286+
fn push_pool_stats(lines: &mut Vec<String>) {
287+
let pool_stats = get_pool_stats();
288+
for (pool, stats) in pool_stats.iter() {
289+
for (name, value) in stats.iter() {
290+
if let Some(prometheus_metric) = PrometheusMetric::<i64>::from_pool(pool, name, *value)
291+
{
292+
lines.push(prometheus_metric.to_string());
293+
} else {
294+
warn!(
295+
"Metric {} not implemented for ({},{})",
296+
name, pool.0, pool.1
297+
);
298+
}
299+
}
300+
}
301+
}
302+
303+
// Adds relevant metrics shown in a SHOW DATABASES admin command.
304+
fn push_database_stats(lines: &mut Vec<String>) {
305+
for (_, pool) in get_all_pools() {
306+
let pool_config = pool.settings.clone();
307+
for shard in 0..pool.shards() {
308+
for server in 0..pool.servers(shard) {
309+
let address = pool.address(shard, server);
310+
let pool_state = pool.pool_state(shard, server);
311+
312+
let metrics = vec![
313+
("pool_size", pool_config.user.pool_size),
314+
("current_connections", pool_state.connections),
315+
];
316+
for (key, value) in metrics {
317+
if let Some(prometheus_metric) =
318+
PrometheusMetric::<u32>::from_database_info(address, key, value)
319+
{
320+
lines.push(prometheus_metric.to_string());
321+
} else {
322+
warn!("Metric {} not implemented for {}", key, address.name());
323+
}
324+
}
325+
}
326+
}
327+
}
328+
}
329+
330+
// Adds relevant metrics shown in a SHOW SERVERS admin command.
331+
fn push_server_stats(lines: &mut Vec<String>) {
332+
let server_stats = get_server_stats();
333+
let mut server_stats_by_addresses = HashMap::<String, ServerInformation>::new();
334+
for (_, info) in server_stats {
335+
server_stats_by_addresses.insert(info.address_name.clone(), info);
336+
}
337+
338+
for (_, pool) in get_all_pools() {
339+
for shard in 0..pool.shards() {
340+
for server in 0..pool.servers(shard) {
341+
let address = pool.address(shard, server);
342+
if let Some(server_info) = server_stats_by_addresses.get(&address.name()) {
343+
let metrics = [
344+
("bytes_received", server_info.bytes_received),
345+
("bytes_sent", server_info.bytes_sent),
346+
("transaction_count", server_info.transaction_count),
347+
("query_count", server_info.query_count),
348+
("error_count", server_info.error_count),
349+
];
350+
for (key, value) in metrics {
351+
if let Some(prometheus_metric) =
352+
PrometheusMetric::<u64>::from_server_info(address, key, value)
353+
{
354+
lines.push(prometheus_metric.to_string());
355+
} else {
356+
warn!("Metric {} not implemented for {}", key, address.name());
357+
}
358+
}
359+
}
360+
}
361+
}
362+
}
363+
}
364+
203365
pub async fn start_metric_server(http_addr: SocketAddr) {
204366
let http_service_factory =
205367
make_service_fn(|_conn| async { Ok::<_, hyper::Error>(service_fn(prometheus_stats)) });

0 commit comments

Comments
 (0)