Metrics rethink: counters as the primitive, histogram()/gauge() derived (ticket 022)

Every metric declaration is now explicitly documented and modeled as
a counter: a match is an occurrence/increment, optionally labeled.
The bucket representation (BucketValue { values: Vec<(Sequence,
Timestamp, MetricPrimitive)> }) already was this; what it lacked was
any way to capture a *value* distinct from the occurrence-count-of-1
marker. Adds RegexToMetricPrimitiveMatcher::role (ParamRole::Label,
the default, or ParamRole::Value — at most one per metric, enforced
at declare time) so a metric can designate one param's captured
number as its per-event value instead of a label.

promql.rs/engine.rs add two new PromQL-evaluable forms built on top
of that value, both consumed as regular PromqlSeriesDto series like
sum()/avg()/rate():
- histogram(metric_name[width]): buckets matching events' values into
  fixed-width buckets, one series per bucket labeled `le`.
- gauge(metric_name): the most-recent value per label combination in
  the queried range.

Existing raw counter queries (metrics with only Label-role params,
sum/avg/rate) are unchanged. Profile files saved before this ticket
still load (role defaults to Label via #[serde(default)]). Metrics.tsx
gains a label/value role picker per param, and the Docs panel explains
the model and the two derived query forms.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XFUNZwCvz3kcod3352ZR2o
This commit is contained in:
Hugh Kaznowski
2026-09-05 15:18:25 +01:00
co-authored by Claude Sonnet 5
parent 1d0f324c43
commit 0c57ed2953
7 changed files with 388 additions and 33 deletions
+8 -1
View File
@@ -40,7 +40,7 @@ use std::time::{SystemTime, UNIX_EPOCH};
use serde::{Deserialize, Serialize};
use crate::data_value::{MetricPrimitiveKind, RegexToMetricPrimitiveMatcher};
use crate::data_value::{MetricPrimitiveKind, ParamRole, RegexToMetricPrimitiveMatcher};
use crate::engine::{DeclareMetricRequest, DeclareParamRequest, EngineHandle, EngineMessage};
use crate::settings::Settings;
use tokio::sync::oneshot;
@@ -119,6 +119,10 @@ pub struct MetricParamRecord {
pub param_name: String,
pub capture_group: String,
pub kind: ProfileMetricPrimitiveKind,
/// See `ParamRole`. `#[serde(default)]` so profiles saved before ticket
/// 022 still load, with every param defaulting to `Label`.
#[serde(default)]
pub role: ParamRole,
}
/// A stored metric declaration, matching `DeclareMetricRequest`'s shape (ticket 002).
@@ -141,6 +145,7 @@ impl From<&DeclareMetricRequest> for MetricDeclarationRecord {
param_name: matcher.param_name.clone(),
capture_group: matcher.capture_group.clone(),
kind: matcher.kind.into(),
role: matcher.role,
})
.collect(),
}
@@ -159,6 +164,7 @@ impl From<&MetricDeclarationRecord> for DeclareMetricRequest {
param_name: param.param_name.clone(),
capture_group: param.capture_group.clone(),
kind: param.kind.into(),
role: param.role,
})
.collect(),
}
@@ -708,6 +714,7 @@ mod tests {
param_name: param_name.to_string(),
capture_group: capture_group.to_string(),
kind: MetricPrimitiveKind::Number,
role: ParamRole::Label,
}],
}
}