use opentelemetry::KeyValue;
use opentelemetry::global;
use opentelemetry::metrics::Meter;
use ursula_runtime::RuntimeMetrics;
use ursula_runtime::RuntimeMetricsSnapshot;
pub(crate) fn register(metrics: &RuntimeMetrics) {
let meter = global::meter("ursula-runtime");
per_group_counter(
&meter,
metrics,
"ursula.appends.accepted",
None,
"Appends accepted by the runtime",
|s| &s.per_group_appends,
);
per_group_counter(
&meter,
metrics,
"ursula.mutations.applied",
None,
"Stream mutations applied on core",
|s| &s.per_group_applied_mutations,
);
per_group_counter(
&meter,
metrics,
"ursula.mutation_apply.ns",
Some("ns"),
"Cumulative mutation apply time",
|s| &s.per_group_mutation_apply_ns,
);
per_group_counter(
&meter,
metrics,
"ursula.group_engine_exec.ns",
Some("ns"),
"Cumulative group-engine execution time",
|s| &s.per_group_group_engine_exec_ns,
);
per_group_counter(
&meter,
metrics,
"ursula.raft_apply.entries",
None,
"Raft entries applied",
|s| &s.per_group_raft_apply_entries,
);
per_group_counter(
&meter,
metrics,
"ursula.raft_apply.ns",
Some("ns"),
"Cumulative raft apply time",
|s| &s.per_group_raft_apply_ns,
);
per_group_counter(
&meter,
metrics,
"ursula.wal.batches",
None,
"WAL batches written",
|s| &s.per_group_wal_batches,
);
per_group_counter(
&meter,
metrics,
"ursula.wal.records",
None,
"WAL records written",
|s| &s.per_group_wal_records,
);
per_group_gauge(
&meter,
metrics,
"ursula.group_mailbox.depth",
"Group mailbox depth",
|s| &s.per_group_group_mailbox_depth,
);
let m = metrics.clone();
let _ = meter
.u64_observable_counter("ursula.routed_requests")
.with_description("Requests routed to a core mailbox")
.with_callback(move |observer| observer.observe(m.snapshot().routed_requests, &[]))
.build();
let m = metrics.clone();
let _ = meter
.u64_observable_gauge("ursula.live_read.waiters")
.with_description("Live-read waiters parked across cores")
.with_callback(move |observer| observer.observe(m.snapshot().live_read_waiters, &[]))
.build();
}
fn per_group_counter(
meter: &Meter,
metrics: &RuntimeMetrics,
name: &'static str,
unit: Option<&'static str>,
description: &'static str,
field: fn(&RuntimeMetricsSnapshot) -> &[u64],
) {
let metrics = metrics.clone();
let mut builder = meter
.u64_observable_counter(name)
.with_description(description);
if let Some(unit) = unit {
builder = builder.with_unit(unit);
}
let _ = builder
.with_callback(move |observer| {
for (group, value) in field(&metrics.snapshot()).iter().enumerate() {
observer.observe(*value, &[group_label(group)]);
}
})
.build();
}
fn per_group_gauge(
meter: &Meter,
metrics: &RuntimeMetrics,
name: &'static str,
description: &'static str,
field: fn(&RuntimeMetricsSnapshot) -> &[u64],
) {
let metrics = metrics.clone();
let _ = meter
.u64_observable_gauge(name)
.with_description(description)
.with_callback(move |observer| {
for (group, value) in field(&metrics.snapshot()).iter().enumerate() {
observer.observe(*value, &[group_label(group)]);
}
})
.build();
}
fn group_label(group: usize) -> KeyValue {
KeyValue::new("group_id", i64::try_from(group).unwrap_or(-1))
}