249 lines
9.8 KiB
Rust
249 lines
9.8 KiB
Rust
fn render_misc(app: &App, snapshot: &crate::config::Snapshot, f: &mut Families) {
|
|
let config = &snapshot.envelope.config;
|
|
let settings = &config.prometheus;
|
|
if settings.include_streams {
|
|
for host in &config.streams {
|
|
let protocol = match host.protocol {
|
|
crate::stream::Protocol::Tcp => "tcp",
|
|
crate::stream::Protocol::Udp => "udp",
|
|
};
|
|
f.add(
|
|
"rust_proxy_stream_enabled",
|
|
"gauge",
|
|
"Desired Layer-4 stream enable flag.",
|
|
&labels(&[("stream", &host.id), ("protocol", protocol)]),
|
|
u8::from(host.enabled),
|
|
);
|
|
}
|
|
for host in &app.active_streams {
|
|
let Some(metrics) = app.stream_metrics.get(&host.id) else {
|
|
continue;
|
|
};
|
|
let protocol = match host.protocol {
|
|
crate::stream::Protocol::Tcp => "tcp",
|
|
crate::stream::Protocol::Udp => "udp",
|
|
};
|
|
let l = labels(&[("stream", &host.id), ("protocol", protocol)]);
|
|
let view = metrics.view();
|
|
for (name, kind, value) in [
|
|
("sessions_total", "counter", view.sessions_total),
|
|
(
|
|
"sessions_completed_total",
|
|
"counter",
|
|
view.sessions_completed,
|
|
),
|
|
("active", "gauge", view.active),
|
|
("rejected_total", "counter", view.rejected),
|
|
("errors_total", "counter", view.errors),
|
|
("bytes_in_total", "counter", view.bytes_in),
|
|
("bytes_out_total", "counter", view.bytes_out),
|
|
("packets_in_total", "counter", view.packets_in),
|
|
("packets_out_total", "counter", view.packets_out),
|
|
] {
|
|
f.add(
|
|
&format!("rust_proxy_stream_{name}"),
|
|
kind,
|
|
"Layer-4 stream observation; reset on process restart.",
|
|
&l,
|
|
value,
|
|
);
|
|
}
|
|
f.add(
|
|
"rust_proxy_stream_max_connections",
|
|
"gauge",
|
|
"Configured stream connection/session capacity.",
|
|
&l,
|
|
host.max_connections,
|
|
);
|
|
}
|
|
}
|
|
if settings.include_logging {
|
|
let stats = app.logs.stats();
|
|
f.add(
|
|
"rust_proxy_log_sink_queue_capacity",
|
|
"gauge",
|
|
"Structured log sink queue capacity.",
|
|
"",
|
|
stats.queue_capacity,
|
|
);
|
|
f.add(
|
|
"rust_proxy_log_sink_queue_dropped_total",
|
|
"counter",
|
|
"Structured log entries dropped because the sink queue was full.",
|
|
"",
|
|
stats.queue_dropped,
|
|
);
|
|
f.add(
|
|
"rust_proxy_log_sink_sent_total",
|
|
"counter",
|
|
"Structured log entries delivered to configured sinks.",
|
|
"",
|
|
stats.sent,
|
|
);
|
|
f.add(
|
|
"rust_proxy_log_sink_errors_total",
|
|
"counter",
|
|
"Structured log sink delivery errors.",
|
|
"",
|
|
stats.errors,
|
|
);
|
|
}
|
|
if settings.include_cache_domains {
|
|
// Bounded resident inspector. Counter families are gauges deliberately:
|
|
// these per-domain values reset on config publication, unlike globals.
|
|
let dashboard = app.cache.dashboard(None, 0, 1);
|
|
f.add(
|
|
"rust_proxy_cache_statistics_revision",
|
|
"gauge",
|
|
"Revision that owns domain statistics.",
|
|
"",
|
|
dashboard.statistics_revision,
|
|
);
|
|
for domain in dashboard.domains {
|
|
// Only configured names, never arbitrary Host input or unknown scope.
|
|
if !config
|
|
.vhosts
|
|
.iter()
|
|
.any(|h| h.id == domain.vhost && h.frontend.hosts.contains(&domain.domain))
|
|
{
|
|
continue;
|
|
}
|
|
let l = labels(&[("vhost", &domain.vhost), ("domain", &domain.domain)]);
|
|
for (name, n) in [
|
|
("objects", domain.entries as u64),
|
|
("hot_objects", domain.hot_entries as u64),
|
|
("resident_bytes", domain.resident_bytes as u64),
|
|
("cold_objects", domain.cold_entries as u64),
|
|
("cold_bytes", domain.cold_bytes),
|
|
("hits", domain.counters.hits),
|
|
("misses", domain.counters.misses),
|
|
("bypasses", domain.counters.bypasses),
|
|
("stored", domain.counters.stored),
|
|
("saved_body_bytes", domain.counters.saved_body_bytes),
|
|
] {
|
|
f.add(
|
|
&format!("rust_proxy_cache_domain_{name}"),
|
|
"gauge",
|
|
"Domain cache observation since current configuration revision.",
|
|
&l,
|
|
n,
|
|
);
|
|
}
|
|
f.add(
|
|
"rust_proxy_cache_domain_hit_ratio",
|
|
"gauge",
|
|
"Domain HIT ratio 0..1.",
|
|
&l,
|
|
domain.hit_ratio,
|
|
);
|
|
for (reason, count) in domain.counters.reasons {
|
|
f.add(
|
|
"rust_proxy_cache_domain_decisions",
|
|
"gauge",
|
|
"Decision reasons since config publication.",
|
|
&format!("{l},reason=\"{}\"", escaped(&reason)),
|
|
count,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn render_histograms(
|
|
snapshot: &crate::config::Snapshot,
|
|
docker_targets: &DockerTargets<'_>,
|
|
out: &mut String,
|
|
) {
|
|
let config = &snapshot.envelope.config;
|
|
let settings = &config.prometheus;
|
|
if settings.include_vhosts {
|
|
out.push_str("# HELP rust_proxy_vhost_exchange_duration_seconds Full exchange lifetime including streaming and cancellation.\n# TYPE rust_proxy_vhost_exchange_duration_seconds histogram\n");
|
|
for (id, m) in &snapshot.vhost_metrics {
|
|
if docker_targets.contains_key(id) {
|
|
continue;
|
|
}
|
|
let l = labels(&[("vhost", id)]);
|
|
let _ = writeln!(
|
|
out,
|
|
"rust_proxy_vhost_exchange_duration_seconds_sum{{{l}}} {:.6}",
|
|
m.duration_us.load(Ordering::Relaxed) as f64 / 1e6
|
|
);
|
|
// Read the cumulative last bucket once for +Inf and count.
|
|
let count =
|
|
m.buckets[crate::metrics::DURATION_BUCKETS_US.len()].load(Ordering::Relaxed);
|
|
let mut previous = 0;
|
|
for (i, threshold) in crate::metrics::DURATION_BUCKETS_US.iter().enumerate() {
|
|
let value = m.buckets[i]
|
|
.load(Ordering::Relaxed)
|
|
.max(previous)
|
|
.min(count);
|
|
previous = value;
|
|
let _ = writeln!(
|
|
out,
|
|
"rust_proxy_vhost_exchange_duration_seconds_bucket{{{l},le=\"{}\"}} {value}",
|
|
*threshold as f64 / 1e6
|
|
);
|
|
}
|
|
let _ = writeln!(out, "rust_proxy_vhost_exchange_duration_seconds_bucket{{{l},le=\"+Inf\"}} {count}\nrust_proxy_vhost_exchange_duration_seconds_count{{{l}}} {count}");
|
|
}
|
|
}
|
|
if settings.include_docker_vhosts {
|
|
out.push_str("# HELP rust_proxy_docker_vhost_exchange_duration_seconds Full Docker-vhost exchange lifetime including streaming and cancellation.\n# TYPE rust_proxy_docker_vhost_exchange_duration_seconds histogram\n");
|
|
for (id, m) in &snapshot.vhost_metrics {
|
|
let Some((agent, assignment)) = docker_targets.get(id) else {
|
|
continue;
|
|
};
|
|
let l = labels(&[
|
|
("agent", &agent.id),
|
|
("vhost", id),
|
|
("service", &assignment.service_key),
|
|
]);
|
|
let _ = writeln!(
|
|
out,
|
|
"rust_proxy_docker_vhost_exchange_duration_seconds_sum{{{l}}} {:.6}",
|
|
m.duration_us.load(Ordering::Relaxed) as f64 / 1e6
|
|
);
|
|
let count =
|
|
m.buckets[crate::metrics::DURATION_BUCKETS_US.len()].load(Ordering::Relaxed);
|
|
let mut previous = 0;
|
|
for (i, threshold) in crate::metrics::DURATION_BUCKETS_US.iter().enumerate() {
|
|
let value = m.buckets[i]
|
|
.load(Ordering::Relaxed)
|
|
.max(previous)
|
|
.min(count);
|
|
previous = value;
|
|
let _ = writeln!(
|
|
out,
|
|
"rust_proxy_docker_vhost_exchange_duration_seconds_bucket{{{l},le=\"{}\"}} {value}",
|
|
*threshold as f64 / 1e6
|
|
);
|
|
}
|
|
let _ = writeln!(out, "rust_proxy_docker_vhost_exchange_duration_seconds_bucket{{{l},le=\"+Inf\"}} {count}\nrust_proxy_docker_vhost_exchange_duration_seconds_count{{{l}}} {count}");
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn render(app: &App) -> String {
|
|
let snapshot = app.config.load_full();
|
|
let config = &snapshot.envelope.config;
|
|
let docker_targets = docker_targets(config);
|
|
let mut out = app.metrics.prometheus(
|
|
app.logs.dropped.load(Ordering::Relaxed),
|
|
snapshot.envelope.revision,
|
|
);
|
|
out.push_str(&app.cache.prometheus());
|
|
if config.prometheus.include_system {
|
|
out.push_str(&app.diagnostics.prometheus());
|
|
}
|
|
let mut families = Families::default();
|
|
render_core(app, &snapshot, &mut out, &mut families);
|
|
render_vhosts(&snapshot, &mut families);
|
|
render_traffic(app, &snapshot, &mut families);
|
|
render_backends(&snapshot, &docker_targets, &mut families);
|
|
render_docker(&snapshot, &docker_targets, &mut families);
|
|
render_misc(app, &snapshot, &mut families);
|
|
families.finish(&mut out);
|
|
render_histograms(&snapshot, &docker_targets, &mut out);
|
|
out
|
|
}
|