Make elapsed-since-last an explicit param extractor, not a Duration-kind special case (ticket 038)

Previously a Value-role param with kind: Duration silently meant "elapsed
time since the previous same-labeled event" instead of parsed text — the
same MetricPrimitiveKind::Duration variant meant two unrelated things
depending on which list (label_params vs value_param) referenced it.

Add ParamDeclaration::elapsed_since_last, a third extraction source
alongside capture_group/json_field_path: when set, the param is computed
(via a new resolve_param_value, used uniformly for label_params and
value_param) rather than text-extracted, and declare_param validates it's
paired with kind: Duration. apply_metric_to_log no longer branches on
(role, kind) — it dispatches purely on what each resolved param declares.

Migrate existing profiles: migrate_duration_value_params converts any
Duration-kind param referenced as a metric's value_param into the new
extractor, and resolve_or_create_legacy_param (ticket 037's pre-existing
migration) now carries the same meaning forward for pre-037 inline
Value-role Duration matchers.

Params.tsx offers "Elapsed since previous event" as its own Extraction
option (hides the now-irrelevant message-match-regex/capture-group
fields and forces kind to duration); Metrics.tsx labels such params
accordingly wherever they're listed as label/value param choices.

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 23:01:32 +01:00
co-authored by Claude Sonnet 5
parent 375aa38ef8
commit 5e30e02a78
8 changed files with 352 additions and 105 deletions
+3 -3
View File
@@ -254,7 +254,7 @@ export function Metrics({ range, activePreset }: { range: TimeRange | null; acti
onChange={() => toggleLabelParam(p.name)}
/>
<span className="font-mono">{p.name}</span>
<span className="text-text-faint">({p.kind})</span>
<span className="text-text-faint">({p.elapsed_since_last ? 'elapsed since previous' : p.kind})</span>
</label>
))
)}
@@ -263,7 +263,7 @@ export function Metrics({ range, activePreset }: { range: TimeRange | null; acti
<div>
<label
className="mb-1 block text-xs text-text-subtle"
title="At most one param becomes the counter's per-event captured value (used by histogram()/gauge()) instead of a label. A 'duration'-kind value param is derived automatically — elapsed time since the previous match with the same labels — rather than parsed from its capture group."
title="At most one param becomes the counter's per-event captured value (used by histogram()/gauge()) instead of a label. An 'elapsed since previous event' param (declared in the Params panel) is computed automatically — elapsed time since the previous match with the same labels — rather than parsed from its capture group."
>
Value param
</label>
@@ -275,7 +275,7 @@ export function Metrics({ range, activePreset }: { range: TimeRange | null; acti
<option value="">(none — occurrence count)</option>
{params.map((p) => (
<option key={p.name} value={p.name}>
{p.name} ({p.kind})
{p.name} ({p.elapsed_since_last ? 'elapsed since previous' : p.kind})
</option>
))}
</select>