otel_rate
Computes the per-second rate from an OpenTelemetry type=sum metric.
The binder desugars otel_rate($raw) into 4 column inputs (value, start_time, timestamp, aggregation_temporality) so column pruning knows what the scan must provide. The accumulator reads its fields by name either way, addressing each through the field's own KIX rather than materializing the row. For cumulative sums, uses start_time for deterministic counter-reset detection. For delta sums, sums the deltas over the time span. Returns null if rows have mixed temporalities. (bzrk extension)
The argument is a dynamic of metric shape — the field set produced by the
OTel-to-bzrk mapping (timestamp, value, start_time,
aggregation_temporality, metric_hash, …) — of which $raw over a metric
table is the usual source. Every field the aggregate needs, the series key
included, is read from that one argument.
Series are detected via metric_hash (#4072), so a group holding many series
— the usual multi-pod dashboard bin — is handled correctly: each series is
reduced independently over its own observed span, a restarted epoch
(start_time change) within a series counts in full, and the per-series rates
are summed. by metric_hash is therefore no longer required for correctness;
add it only when you want one output row per series. A series with a single
observation in the group defines no rate and contributes nothing; when no
series has two observations the result is null.
Rows without a metric_hash column share one pseudo-series, which is what a
datatable literal or an explicit project that drops the column gets.
The optional second parameter is the rate denominator — per-second versus
per-minute — and not a window. otel_rate($raw, 30d) is "the per-series rate,
expressed per 30 days", so a series observed for a few minutes still claims
the full 30 days and the group total runs high by the restart count. For a
total over the window use otel_increase, which sums each series'
contribution without dividing by a span.
In make-series, a bin that no observation reached is null, not zero: a
rate of zero says the counter was seen at both ends of the bin and did not
move, which is a different claim from having no sample there (#4993). A counter
observed to be flat therefore still reports 0, and a group this aggregate
cannot answer for — mixed temporalities — is null across every bin rather than
a flat zero line.
otel_rate is itself an aggregate: use it directly as a summarize/make-series output — never nested inside another aggregate such as avg(...) or sum(...) (that is a WRONG FUNCTION CONTEXT error).
Syntax
otel_rate($raw)Parameters
Prop
Type
Returns: real
Syntax
otel_rate($raw, per_duration)Parameters
Prop
Type
Returns: real
Examples
Example 1
// total rate across every series, per bin — series are split internally
OtelMetrics
| where metric_type == "sum"
| summarize rate = otel_rate($raw) by bin(timestamp, 1m)| timestamp (datetime) | rate (real) |
|---|---|
| 2024-01-01T00:00:00Z | null |
| 2024-01-01T00:01:00Z | null |
| 2024-01-01T00:02:00Z | null |
Example 2
OtelMetrics
| where metric_type == "sum"
| summarize otel_rate($raw, 1m) by metric_name| metric_name (dynamic) | otel_rate_$raw (real) |
|---|---|
| "http.requests.total" | 6000.0 |
Example 3
// one row per series: group by metric_hash when you want them broken out
OtelMetrics
| where metric_name == "http.server.request.count"
| summarize rate = otel_rate($raw) by metric_hash, bin(timestamp, 5m)| metric_hash (dynamic) | timestamp (datetime) | rate (real) |
|---|