Skip to content

Commit f0865ca

Browse files
drdrshmyzie
andauthored
Improve Prometheus exporter output (#795)
* Prometheus metrics updates: * Add username label to deconflict metrics that would otherwise have duplicate labels across different pools. * Group metrics by name and only print HELP and TYPE once per metric name. * Sort labels for a deterministic output. --------- Co-authored-by: Curtis Myzie <[email protected]> Co-authored-by: Towhid Khan
1 parent 7d047c6 commit f0865ca

File tree

1 file changed

+69
-14
lines changed

1 file changed

+69
-14
lines changed

src/prometheus.rs

Lines changed: 69 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -200,18 +200,17 @@ struct PrometheusMetric<Value: fmt::Display> {
200200

201201
impl<Value: fmt::Display> fmt::Display for PrometheusMetric<Value> {
202202
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
203-
let formatted_labels = self
204-
.labels
203+
let mut sorted_labels: Vec<_> = self.labels.iter().collect();
204+
sorted_labels.sort_by_key(|&(key, _)| key);
205+
let formatted_labels = sorted_labels
205206
.iter()
206207
.map(|(key, value)| format!("{}=\"{}\"", key, value))
207208
.collect::<Vec<_>>()
208209
.join(",");
209210
write!(
210211
f,
211-
"# HELP {name} {help}\n# TYPE {name} {ty}\n{name}{{{formatted_labels}}} {value}\n",
212+
"{name}{{{formatted_labels}}} {value}",
212213
name = format_args!("pgcat_{}", self.name),
213-
help = self.help,
214-
ty = self.ty,
215214
formatted_labels = formatted_labels,
216215
value = self.value
217216
)
@@ -247,7 +246,7 @@ impl<Value: fmt::Display> PrometheusMetric<Value> {
247246
labels.insert("pool", address.pool_name.clone());
248247
labels.insert("index", address.address_index.to_string());
249248
labels.insert("database", address.database.to_string());
250-
labels.insert("user", address.username.clone());
249+
labels.insert("username", address.username.clone());
251250

252251
Self::from_name(&format!("databases_{}", name), value, labels)
253252
}
@@ -264,7 +263,8 @@ impl<Value: fmt::Display> PrometheusMetric<Value> {
264263
labels.insert("pool", address.pool_name.clone());
265264
labels.insert("index", address.address_index.to_string());
266265
labels.insert("database", address.database.to_string());
267-
labels.insert("user", address.username.clone());
266+
labels.insert("username", address.username.clone());
267+
268268
Self::from_name(&format!("servers_{}", name), value, labels)
269269
}
270270

@@ -276,7 +276,7 @@ impl<Value: fmt::Display> PrometheusMetric<Value> {
276276
labels.insert("role", address.role.to_string());
277277
labels.insert("index", address.address_index.to_string());
278278
labels.insert("database", address.database.to_string());
279-
labels.insert("user", address.username.clone());
279+
labels.insert("username", address.username.clone());
280280

281281
Self::from_name(&format!("stats_{}", name), value, labels)
282282
}
@@ -288,6 +288,15 @@ impl<Value: fmt::Display> PrometheusMetric<Value> {
288288

289289
Self::from_name(&format!("pools_{}", name), value, labels)
290290
}
291+
292+
fn get_header(&self) -> String {
293+
format!(
294+
"\n# HELP {name} {help}\n# TYPE {name} {ty}",
295+
name = format_args!("pgcat_{}", self.name),
296+
help = self.help,
297+
ty = self.ty,
298+
)
299+
}
291300
}
292301

293302
async fn prometheus_stats(
@@ -313,6 +322,7 @@ async fn prometheus_stats(
313322

314323
// Adds metrics shown in a SHOW STATS admin command.
315324
fn push_address_stats(lines: &mut Vec<String>) {
325+
let mut grouped_metrics: HashMap<String, Vec<PrometheusMetric<u64>>> = HashMap::new();
316326
for (_, pool) in get_all_pools() {
317327
for shard in 0..pool.shards() {
318328
for server in 0..pool.servers(shard) {
@@ -322,41 +332,64 @@ fn push_address_stats(lines: &mut Vec<String>) {
322332
if let Some(prometheus_metric) =
323333
PrometheusMetric::<u64>::from_address(address, &key, value)
324334
{
325-
lines.push(prometheus_metric.to_string());
335+
grouped_metrics
336+
.entry(key)
337+
.or_default()
338+
.push(prometheus_metric);
326339
} else {
327340
debug!("Metric {} not implemented for {}", key, address.name());
328341
}
329342
}
330343
}
331344
}
332345
}
346+
for (_key, metrics) in grouped_metrics {
347+
if !metrics.is_empty() {
348+
lines.push(metrics[0].get_header());
349+
for metric in metrics {
350+
lines.push(metric.to_string());
351+
}
352+
}
353+
}
333354
}
334355

335356
// Adds relevant metrics shown in a SHOW POOLS admin command.
336357
fn push_pool_stats(lines: &mut Vec<String>) {
358+
let mut grouped_metrics: HashMap<String, Vec<PrometheusMetric<u64>>> = HashMap::new();
337359
let pool_stats = PoolStats::construct_pool_lookup();
338360
for (pool_id, stats) in pool_stats.iter() {
339361
for (name, value) in stats.clone() {
340362
if let Some(prometheus_metric) =
341363
PrometheusMetric::<u64>::from_pool(pool_id.clone(), &name, value)
342364
{
343-
lines.push(prometheus_metric.to_string());
365+
grouped_metrics
366+
.entry(name)
367+
.or_default()
368+
.push(prometheus_metric);
344369
} else {
345370
debug!("Metric {} not implemented for ({})", name, *pool_id);
346371
}
347372
}
348373
}
374+
for (_key, metrics) in grouped_metrics {
375+
if !metrics.is_empty() {
376+
lines.push(metrics[0].get_header());
377+
for metric in metrics {
378+
lines.push(metric.to_string());
379+
}
380+
}
381+
}
349382
}
350383

351384
// Adds relevant metrics shown in a SHOW DATABASES admin command.
352385
fn push_database_stats(lines: &mut Vec<String>) {
386+
let mut grouped_metrics: HashMap<String, Vec<PrometheusMetric<u32>>> = HashMap::new();
353387
for (_, pool) in get_all_pools() {
354388
let pool_config = pool.settings.clone();
355389
for shard in 0..pool.shards() {
356390
for server in 0..pool.servers(shard) {
357391
let address = pool.address(shard, server);
358392
let pool_state = pool.pool_state(shard, server);
359-
360393
let metrics = vec![
361394
("pool_size", pool_config.user.pool_size),
362395
("current_connections", pool_state.connections),
@@ -365,14 +398,25 @@ fn push_database_stats(lines: &mut Vec<String>) {
365398
if let Some(prometheus_metric) =
366399
PrometheusMetric::<u32>::from_database_info(address, key, value)
367400
{
368-
lines.push(prometheus_metric.to_string());
401+
grouped_metrics
402+
.entry(key.to_string())
403+
.or_default()
404+
.push(prometheus_metric);
369405
} else {
370406
debug!("Metric {} not implemented for {}", key, address.name());
371407
}
372408
}
373409
}
374410
}
375411
}
412+
for (_key, metrics) in grouped_metrics {
413+
if !metrics.is_empty() {
414+
lines.push(metrics[0].get_header());
415+
for metric in metrics {
416+
lines.push(metric.to_string());
417+
}
418+
}
419+
}
376420
}
377421

378422
// Adds relevant metrics shown in a SHOW SERVERS admin command.
@@ -405,7 +449,7 @@ fn push_server_stats(lines: &mut Vec<String>) {
405449
crate::stats::ServerState::Idle => entry.idle_count += 1,
406450
}
407451
}
408-
452+
let mut grouped_metrics: HashMap<String, Vec<PrometheusMetric<u64>>> = HashMap::new();
409453
for (_, pool) in get_all_pools() {
410454
for shard in 0..pool.shards() {
411455
for server in 0..pool.servers(shard) {
@@ -428,7 +472,10 @@ fn push_server_stats(lines: &mut Vec<String>) {
428472
if let Some(prometheus_metric) =
429473
PrometheusMetric::<u64>::from_server_info(address, key, value)
430474
{
431-
lines.push(prometheus_metric.to_string());
475+
grouped_metrics
476+
.entry(key.to_string())
477+
.or_default()
478+
.push(prometheus_metric);
432479
} else {
433480
debug!("Metric {} not implemented for {}", key, address.name());
434481
}
@@ -437,6 +484,14 @@ fn push_server_stats(lines: &mut Vec<String>) {
437484
}
438485
}
439486
}
487+
for (_key, metrics) in grouped_metrics {
488+
if !metrics.is_empty() {
489+
lines.push(metrics[0].get_header());
490+
for metric in metrics {
491+
lines.push(metric.to_string());
492+
}
493+
}
494+
}
440495
}
441496

442497
pub async fn start_metric_server(http_addr: SocketAddr) {

0 commit comments

Comments
 (0)