119 lines
4.0 KiB
Rust
119 lines
4.0 KiB
Rust
use std::time::Duration;
|
|
|
|
use opentelemetry::{KeyValue, global, metrics::Counter};
|
|
|
|
use crate::SpacetimeClientError;
|
|
|
|
// SpacetimeDB procedure 指标只使用 procedure / status_class 等低基数字段,避免把用户或作品 ID 写进指标标签。
|
|
pub(crate) struct ProcedureMetricsGuard {
|
|
procedure: &'static str,
|
|
started_at: std::time::Instant,
|
|
}
|
|
|
|
pub(crate) struct ReadMetricsGuard {
|
|
read: &'static str,
|
|
started_at: std::time::Instant,
|
|
}
|
|
|
|
pub(crate) fn begin_procedure(procedure: &'static str) -> ProcedureMetricsGuard {
|
|
ProcedureMetricsGuard {
|
|
procedure,
|
|
started_at: std::time::Instant::now(),
|
|
}
|
|
}
|
|
|
|
pub(crate) fn begin_read(read: &'static str) -> ReadMetricsGuard {
|
|
ReadMetricsGuard {
|
|
read,
|
|
started_at: std::time::Instant::now(),
|
|
}
|
|
}
|
|
|
|
impl ProcedureMetricsGuard {
|
|
pub(crate) fn finish<T>(&self, result: &Result<T, SpacetimeClientError>) {
|
|
let duration = self.started_at.elapsed();
|
|
record_procedure(self.procedure, duration, result.is_err());
|
|
}
|
|
}
|
|
|
|
impl ReadMetricsGuard {
|
|
pub(crate) fn finish<T>(&self, result: &Result<T, SpacetimeClientError>) {
|
|
let duration = self.started_at.elapsed();
|
|
record_read(self.read, duration, result.is_err());
|
|
}
|
|
}
|
|
|
|
struct SpacetimeMetrics {
|
|
calls: Counter<u64>,
|
|
errors: Counter<u64>,
|
|
duration_ms: opentelemetry::metrics::Histogram<f64>,
|
|
read_calls: Counter<u64>,
|
|
read_errors: Counter<u64>,
|
|
read_duration_ms: opentelemetry::metrics::Histogram<f64>,
|
|
}
|
|
|
|
fn spacetime_metrics() -> &'static SpacetimeMetrics {
|
|
static METRICS: std::sync::OnceLock<SpacetimeMetrics> = std::sync::OnceLock::new();
|
|
METRICS.get_or_init(|| {
|
|
let meter = global::meter("genarrative-spacetime-client");
|
|
SpacetimeMetrics {
|
|
calls: meter
|
|
.u64_counter("genarrative.spacetime.procedure.calls")
|
|
.with_description("SpacetimeDB procedure call count")
|
|
.build(),
|
|
errors: meter
|
|
.u64_counter("genarrative.spacetime.procedure.errors")
|
|
.with_description("SpacetimeDB procedure error count")
|
|
.build(),
|
|
duration_ms: meter
|
|
.f64_histogram("genarrative.spacetime.procedure.duration_ms")
|
|
.with_unit("ms")
|
|
.with_description("SpacetimeDB procedure duration in milliseconds")
|
|
.build(),
|
|
read_calls: meter
|
|
.u64_counter("genarrative.spacetime.read.calls")
|
|
.with_description("SpacetimeDB local subscription cache read count")
|
|
.build(),
|
|
read_errors: meter
|
|
.u64_counter("genarrative.spacetime.read.errors")
|
|
.with_description("SpacetimeDB local subscription cache read error count")
|
|
.build(),
|
|
read_duration_ms: meter
|
|
.f64_histogram("genarrative.spacetime.read.duration_ms")
|
|
.with_unit("ms")
|
|
.with_description("SpacetimeDB local subscription cache read duration in milliseconds")
|
|
.build(),
|
|
}
|
|
})
|
|
}
|
|
|
|
fn record_procedure(procedure: &'static str, duration: Duration, failed: bool) {
|
|
let labels = vec![
|
|
KeyValue::new("procedure", procedure),
|
|
KeyValue::new("status_class", if failed { "error" } else { "ok" }),
|
|
];
|
|
let metrics = spacetime_metrics();
|
|
metrics.calls.add(1, &labels);
|
|
metrics
|
|
.duration_ms
|
|
.record(duration.as_secs_f64() * 1000.0, &labels);
|
|
if failed {
|
|
metrics.errors.add(1, &labels);
|
|
}
|
|
}
|
|
|
|
fn record_read(read: &'static str, duration: Duration, failed: bool) {
|
|
let labels = vec![
|
|
KeyValue::new("read", read),
|
|
KeyValue::new("status_class", if failed { "error" } else { "ok" }),
|
|
];
|
|
let metrics = spacetime_metrics();
|
|
metrics.read_calls.add(1, &labels);
|
|
metrics
|
|
.read_duration_ms
|
|
.record(duration.as_secs_f64() * 1000.0, &labels);
|
|
if failed {
|
|
metrics.read_errors.add(1, &labels);
|
|
}
|
|
}
|