diff --git a/src/prometheus.rs b/src/prometheus.rs index 9a14b8f..93348b8 100644 --- a/src/prometheus.rs +++ b/src/prometheus.rs @@ -200,18 +200,17 @@ struct PrometheusMetric { impl fmt::Display for PrometheusMetric { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let formatted_labels = self - .labels + let mut sorted_labels: Vec<_> = self.labels.iter().collect(); + sorted_labels.sort_by_key(|&(key, _)| key); + let formatted_labels = sorted_labels .iter() .map(|(key, value)| format!("{}=\"{}\"", key, value)) .collect::>() .join(","); write!( f, - "# HELP {name} {help}\n# TYPE {name} {ty}\n{name}{{{formatted_labels}}} {value}\n", + "{name}{{{formatted_labels}}} {value}", name = format_args!("pgcat_{}", self.name), - help = self.help, - ty = self.ty, formatted_labels = formatted_labels, value = self.value ) @@ -247,7 +246,7 @@ impl PrometheusMetric { labels.insert("pool", address.pool_name.clone()); labels.insert("index", address.address_index.to_string()); labels.insert("database", address.database.to_string()); - labels.insert("user", address.username.clone()); + labels.insert("username", address.username.clone()); Self::from_name(&format!("databases_{}", name), value, labels) } @@ -264,7 +263,8 @@ impl PrometheusMetric { labels.insert("pool", address.pool_name.clone()); labels.insert("index", address.address_index.to_string()); labels.insert("database", address.database.to_string()); - labels.insert("user", address.username.clone()); + labels.insert("username", address.username.clone()); + Self::from_name(&format!("servers_{}", name), value, labels) } @@ -276,7 +276,7 @@ impl PrometheusMetric { labels.insert("role", address.role.to_string()); labels.insert("index", address.address_index.to_string()); labels.insert("database", address.database.to_string()); - labels.insert("user", address.username.clone()); + labels.insert("username", address.username.clone()); Self::from_name(&format!("stats_{}", name), value, labels) } @@ -288,6 +288,15 @@ impl PrometheusMetric { Self::from_name(&format!("pools_{}", name), value, labels) } + + fn get_header(&self) -> String { + format!( + "\n# HELP {name} {help}\n# TYPE {name} {ty}", + name = format_args!("pgcat_{}", self.name), + help = self.help, + ty = self.ty, + ) + } } async fn prometheus_stats( @@ -313,6 +322,7 @@ async fn prometheus_stats( // Adds metrics shown in a SHOW STATS admin command. fn push_address_stats(lines: &mut Vec) { + let mut grouped_metrics: HashMap>> = HashMap::new(); for (_, pool) in get_all_pools() { for shard in 0..pool.shards() { for server in 0..pool.servers(shard) { @@ -322,7 +332,10 @@ fn push_address_stats(lines: &mut Vec) { if let Some(prometheus_metric) = PrometheusMetric::::from_address(address, &key, value) { - lines.push(prometheus_metric.to_string()); + grouped_metrics + .entry(key) + .or_default() + .push(prometheus_metric); } else { debug!("Metric {} not implemented for {}", key, address.name()); } @@ -330,33 +343,53 @@ fn push_address_stats(lines: &mut Vec) { } } } + for (_key, metrics) in grouped_metrics { + if !metrics.is_empty() { + lines.push(metrics[0].get_header()); + for metric in metrics { + lines.push(metric.to_string()); + } + } + } } // Adds relevant metrics shown in a SHOW POOLS admin command. fn push_pool_stats(lines: &mut Vec) { + let mut grouped_metrics: HashMap>> = HashMap::new(); let pool_stats = PoolStats::construct_pool_lookup(); for (pool_id, stats) in pool_stats.iter() { for (name, value) in stats.clone() { if let Some(prometheus_metric) = PrometheusMetric::::from_pool(pool_id.clone(), &name, value) { - lines.push(prometheus_metric.to_string()); + grouped_metrics + .entry(name) + .or_default() + .push(prometheus_metric); } else { debug!("Metric {} not implemented for ({})", name, *pool_id); } } } + for (_key, metrics) in grouped_metrics { + if !metrics.is_empty() { + lines.push(metrics[0].get_header()); + for metric in metrics { + lines.push(metric.to_string()); + } + } + } } // Adds relevant metrics shown in a SHOW DATABASES admin command. fn push_database_stats(lines: &mut Vec) { + let mut grouped_metrics: HashMap>> = HashMap::new(); for (_, pool) in get_all_pools() { let pool_config = pool.settings.clone(); for shard in 0..pool.shards() { for server in 0..pool.servers(shard) { let address = pool.address(shard, server); let pool_state = pool.pool_state(shard, server); - let metrics = vec![ ("pool_size", pool_config.user.pool_size), ("current_connections", pool_state.connections), @@ -365,7 +398,10 @@ fn push_database_stats(lines: &mut Vec) { if let Some(prometheus_metric) = PrometheusMetric::::from_database_info(address, key, value) { - lines.push(prometheus_metric.to_string()); + grouped_metrics + .entry(key.to_string()) + .or_default() + .push(prometheus_metric); } else { debug!("Metric {} not implemented for {}", key, address.name()); } @@ -373,6 +409,14 @@ fn push_database_stats(lines: &mut Vec) { } } } + for (_key, metrics) in grouped_metrics { + if !metrics.is_empty() { + lines.push(metrics[0].get_header()); + for metric in metrics { + lines.push(metric.to_string()); + } + } + } } // Adds relevant metrics shown in a SHOW SERVERS admin command. @@ -405,7 +449,7 @@ fn push_server_stats(lines: &mut Vec) { crate::stats::ServerState::Idle => entry.idle_count += 1, } } - + let mut grouped_metrics: HashMap>> = HashMap::new(); for (_, pool) in get_all_pools() { for shard in 0..pool.shards() { for server in 0..pool.servers(shard) { @@ -428,7 +472,10 @@ fn push_server_stats(lines: &mut Vec) { if let Some(prometheus_metric) = PrometheusMetric::::from_server_info(address, key, value) { - lines.push(prometheus_metric.to_string()); + grouped_metrics + .entry(key.to_string()) + .or_default() + .push(prometheus_metric); } else { debug!("Metric {} not implemented for {}", key, address.name()); } @@ -437,6 +484,14 @@ fn push_server_stats(lines: &mut Vec) { } } } + for (_key, metrics) in grouped_metrics { + if !metrics.is_empty() { + lines.push(metrics[0].get_header()); + for metric in metrics { + lines.push(metric.to_string()); + } + } + } } pub async fn start_metric_server(http_addr: SocketAddr) {