Skip to main content

LogQL Metric Queries

A metric query applies a function to the result of a log query and returns numbers instead of lines — an error rate, a p99 latency, the noisiest service. Use it with the range query API to graph a result, or the instant query API for a single value.

sum by (service) (rate({job="app"} |= "error" [5m]))

Log range aggregations​

These operate on a log stream over a [duration] window.

FunctionReturns
rate(range)Entries per second
count_over_time(range)Entry count in the window
bytes_rate(range)Bytes per second
bytes_over_time(range)Bytes in the window
absent_over_time(range)1 if the selector matched nothing, else empty
count_over_time({job="mysql"}[5m])
sum by (level) (count_over_time({job="app"} | json [5m]))

offset modifier​

Shift the window back in time. It must come right after the range selector:

count_over_time({job="mysql"}[5m] offset 5m) # valid
count_over_time({job="mysql"}[5m]) offset 5m # invalid

Unwrapped range aggregations​

Add | unwrap <label> to turn an extracted label into the sample value, then aggregate it. Conversion helpers: unwrap duration(field), unwrap duration_seconds(field), unwrap bytes(field).

FunctionReturns
rate(range)Per-second rate of the summed values
rate_counter(range)Per-second rate, treating values as a counter
sum_over_time(range)Sum of values
avg_over_time(range)Mean
min_over_time / max_over_timeExtremes
first_over_time / last_over_timeEdge values
stddev_over_time / stdvar_over_timeSpread
quantile_over_time(φ, range)φ-quantile, 0 ≤ φ ≤ 1
absent_over_time(range)1 if no data, else empty
# p99 request duration by route
quantile_over_time(0.99, {job="app"} | json | unwrap duration_ms [5m]) by (route)

# Bytes written per second, from a logged field
sum by (pod) (rate({job="app"} | logfmt | unwrap bytes(out) [1m]))

Grouping syntax:

<aggr>([param,] <unwrapped-range>) [by (labels) | without (labels)]

Vector aggregation operators​

Wrap a range aggregation to combine series.

OperatorEffect
sumTotal
avgMean
min / maxExtremes
countNumber of series
stddev / stdvarSpread
topk(k, …) / bottomk(k, …)k largest / smallest series
sort / sort_descOrder the result (instant queries only)
topk(10, sum by (path) (rate({job="nginx"} | json [5m])))
sum by (service) (rate({job="app"} |= "error" [5m]))

by (labels) keeps only the listed labels; without (labels) drops them.

vector​

vector(s) returns scalar s as a labelless series — handy as an or fallback so a panel shows 0 rather than "No data":

sum(count_over_time({namespace="traefik"}[5m])) or vector(0)

approx_topk​

approx_topk(k, <vector>) approximates topk cheaply over very large result sets. Instant queries only, no grouping.


Binary operators​

Combine two metric queries, or a query and a scalar.

GroupOperators
Arithmetic+ - * / % ^
Comparison== != > >= < <=
Logical / setand or unless
# error ratio
sum(rate({app="foo"} |= "error" [5m])) / sum(rate({app="foo"} [5m]))

# streams that logged more than 10 lines in the last minute
count_over_time({foo="bar"}[1m]) > 10

Precedence, high to low: ^, then * / %, then + -, then comparison, then and unless, then or. All are left-associative except ^.

bool modifier​

By default a comparison filters series. Add bool after the operator to keep every series and set its value to 1 (passes) or 0 (fails) instead.

count_over_time({foo="bar"}[1m]) > bool 10

Vector matching​

When both sides are vectors, elements are matched by identical label sets. Adjust the match with on / ignoring, and fan out one-to-many with group_left / group_right.

# match only on the `app` label
sum by (app, status) (rate({job="http"} | json [5m]))
/ on (app) group_left
sum by (app) (rate({job="http"} | json [5m]))

on (labels) matches on only those labels; ignoring (labels) matches on all but those. group_left(extra) / group_right(extra) name the higher-cardinality side and carry extra labels through from the other side. Grouping modifiers work with arithmetic and comparison operators only.


label_replace​

Rewrite or add a label on each series in an instant vector.

label_replace(v, dst_label, replacement, src_label, regex)

regex is matched against src_label; on a match, dst_label is set to replacement with $1, $2… expanded from the capture groups. No match leaves the series untouched.

label_replace(
rate({job="api", service="a:c"} |= "err" [1m]),
"team", "$1", "service", "(.*):.*")

Result ordering​

Metric results come back in no particular order unless the outer operator is sort or sort_desc, and those only apply to instant queries. For range queries, sort client-side.