use std::{collections::BTreeMap, fmt::Write};
use super::{DurationMetricValue, escape_label, write_duration_metric};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct RedisMetricLabels {
pub command: String,
pub shard: String,
pub result: String,
}
impl RedisMetricLabels {
pub fn new(
command: impl Into<String>,
shard: impl Into<String>,
result: impl Into<String>,
) -> Self {
Self {
command: command.into(),
shard: shard.into(),
result: result.into(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct RedisEventLabels {
pub event: String,
pub shard: String,
pub result: String,
}
impl RedisEventLabels {
pub fn new(
event: impl Into<String>,
shard: impl Into<String>,
result: impl Into<String>,
) -> Self {
Self {
event: event.into(),
shard: shard.into(),
result: result.into(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct RedisDegradationLabels {
pub operation: String,
pub action: String,
pub shard: String,
}
impl RedisDegradationLabels {
pub fn new(
operation: impl Into<String>,
action: impl Into<String>,
shard: impl Into<String>,
) -> Self {
Self {
operation: operation.into(),
action: action.into(),
shard: shard.into(),
}
}
}
pub(crate) fn render(
output: &mut String,
metrics: &BTreeMap<RedisMetricLabels, DurationMetricValue>,
events: &BTreeMap<RedisEventLabels, u64>,
degradations: &BTreeMap<RedisDegradationLabels, u64>,
) {
output.push_str("# HELP rs_zero_redis_commands_total Total number of Redis commands.\n");
output.push_str("# TYPE rs_zero_redis_commands_total counter\n");
for (labels, value) in metrics {
write!(output, "rs_zero_redis_commands_total{{").ok();
write_labels(output, labels, None);
writeln!(output, "}} {}", value.count).ok();
}
output.push_str(
"# HELP rs_zero_redis_command_errors_total Total number of failed Redis commands.\n",
);
output.push_str("# TYPE rs_zero_redis_command_errors_total counter\n");
for (labels, value) in metrics
.iter()
.filter(|(labels, _)| labels.result != "success")
{
write!(output, "rs_zero_redis_command_errors_total{{").ok();
write_labels(output, labels, None);
writeln!(output, "}} {}", value.count).ok();
}
output.push_str("# HELP rs_zero_redis_command_duration_seconds Redis command duration.\n");
output.push_str("# TYPE rs_zero_redis_command_duration_seconds histogram\n");
for (labels, value) in metrics {
write_duration_metric(
output,
"rs_zero_redis_command_duration_seconds",
labels,
value,
write_labels,
);
}
output.push_str("# HELP rs_zero_redis_events_total Total number of Redis pool, redirect, script and degradation events.\n");
output.push_str("# TYPE rs_zero_redis_events_total counter\n");
for (labels, value) in events {
write!(output, "rs_zero_redis_events_total{{").ok();
write_event_labels(output, labels);
writeln!(output, "}} {}", value).ok();
}
output.push_str("# HELP rs_zero_redis_degradations_total Total number of explicitly degraded Redis operations.\n");
output.push_str("# TYPE rs_zero_redis_degradations_total counter\n");
for (labels, value) in degradations {
write!(output, "rs_zero_redis_degradations_total{{").ok();
write_degradation_labels(output, labels);
writeln!(output, "}} {}", value).ok();
}
}
fn write_labels(output: &mut String, labels: &RedisMetricLabels, le: Option<&str>) {
write!(
output,
"command=\"{}\",shard=\"{}\",result=\"{}\"",
escape_label(&labels.command),
escape_label(&labels.shard),
escape_label(&labels.result)
)
.ok();
if let Some(le) = le {
write!(output, ",le=\"{}\"", escape_label(le)).ok();
}
}
fn write_event_labels(output: &mut String, labels: &RedisEventLabels) {
write!(
output,
"event=\"{}\",shard=\"{}\",result=\"{}\"",
escape_label(&labels.event),
escape_label(&labels.shard),
escape_label(&labels.result)
)
.ok();
}
fn write_degradation_labels(output: &mut String, labels: &RedisDegradationLabels) {
write!(
output,
"operation=\"{}\",action=\"{}\",shard=\"{}\"",
escape_label(&labels.operation),
escape_label(&labels.action),
escape_label(&labels.shard)
)
.ok();
}