PromQL: Functions
|
This section documents the current Prometheus 3.x line (latest 3.14.0) as published at the Prometheus documentation and the current Grafana 13.x line (latest 13.2.1) as published at the Grafana documentation, which are the references these pages are written and verified against. No specific patch version is pinned. Some capabilities (Grafana Enterprise-only features, Grafana Cloud-hosted services, and long-term-storage systems like Thanos/Cortex/Mimir) are linked, not documented in depth. This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production, since both projects iterate quickly. This section’s bibliography lists the reference material consulted while preparing these pages. |
PromQL: Basics & Selectors introduces range
vectors and instant vectors, and
PromQL: Operators & Aggregation covers
sum by (…) and friends — both are assumed below. This page works through the function calls that
turn those building blocks into the queries actually used in dashboards and alerts: rate(),
irate() and increase() for Counters; histogram_quantile() for turning a classic Histogram’s
_bucket series into a percentile; predict_linear() for forecasting when a Gauge will cross a
threshold; label_replace()/label_join() for reshaping labels with regular expressions; and the
time/date functions. The full function reference is
Functions, and every example below
can be adapted from the worked queries at
Query examples.
Counter functions: rate, irate, increase
A Counter only ever goes up (or resets to zero on restart, as covered in
Data Model & Metric Types), so its raw
value is rarely useful by itself — what dashboards and alerts want is how fast it is increasing.
rate(), irate() and increase() all take a range vector, never an instant vector: a single
scraped sample has no rate, so each of these needs a [5m]-style range selector to compare samples
across a time window.
# Per-second average rate of increase over the last 5 minutes.
rate(http_requests_total{job="api"}[5m])
# https://prometheus.io/docs/prometheus/latest/querying/functions/
rate() looks at the first and last samples in the range, extrapolates slightly to the edges of the
window, and divides by the elapsed seconds — the result is smoothed across the whole range, which is
what makes it the right choice for graphs and for alerting expressions, where a single noisy scrape
should not flap a rule. irate() instead uses only the two most recent samples in the range and
ignores the rest, so it reacts instantly to a spike but is far noisier; use it only for interactive,
high-resolution graphing of fast-moving counters, never in a recording or alerting rule. increase()
is rate() multiplied by the number of seconds in the range — the extrapolated total increase over
the window, not a per-second figure:
# Extrapolated request count over the last hour, not a rate.
increase(http_requests_total{job="api"}[1h])
All three detect a counter reset (the value drops between consecutive samples, typically a process
restart) and correct for it automatically by treating the drop as if the counter had continued from
where it left off, rather than reporting a spurious negative rate. This correction only works because
the input is a genuine Counter; calling rate() on a Gauge produces a meaningless number. Because
rate()/irate()/increase() consume a range vector and return an instant vector, wrap the result
in sum by (…) (…) to aggregate across instances — summing first and taking the rate of the sum
is a different (and usually wrong) query, since it would apply a single reset-correction across
already-summed, cross-instance data:
# Correct: rate per series first, then sum across instances.
sum by (job) (rate(http_requests_total[5m]))
histogram_quantile: latency percentiles from a classic Histogram
A classic Histogram (see Data Model &
Metric Types) exposes cumulative _bucket{le="…"} Counter series alongside _sum and _count.
histogram_quantile(φ, b) estimates the φ-quantile (0 ≤ φ ≤ 1; 0.99 for p99) by linear
interpolation within the bucket where the cumulative count crosses φ of the total, given a vector b
whose series carry an le label:
# p99 request latency over the last 5 minutes, aggregated across all instances of a job.
histogram_quantile(0.99,
sum by (le, job) (rate(http_request_duration_seconds_bucket[5m]))
)
# https://prometheus.io/docs/prometheus/latest/querying/functions/
Read this from the inside out:
-
rate(…_bucket[5m])converts each cumulative bucket Counter into a per-second rate, one series perle/instance/pod combination. -
sum by (le, job) (…)adds those rates across instances, collapsing everything down to one series perlevalue per job —lemust be kept in theby (…)clause, sincehistogram_quantile()needs it to reconstruct the bucket boundaries. -
histogram_quantile(0.99, …)walks the resultinglebuckets for eachjoband interpolates the 0.99 quantile within whichever bucket the running count crosses that threshold.
The estimate is only as accurate as the bucket boundaries chosen at instrumentation time — a p99
that falls between two widely spaced le values is a rough interpolation, not an exact figure, which
is why bucket boundary selection matters and is covered alongside instrumentation in
Instrumenting Applications. Native
histograms let histogram_quantile() operate on a single series instead of a _bucket family and
avoid this interpolation error; see that same page for the trade-off. This is the query pattern
Recording & Alerting Rules precomputes
into a recording rule, since a histogram_quantile(rate(…)) chain evaluated on every dashboard
refresh (or every alert evaluation) is one of the more expensive query shapes to run live.
predict_linear: capacity forecasting
predict_linear(v range-vector, t scalar) fits a simple linear regression to the samples in the
range vector and extrapolates t seconds into the future, returning the predicted value. It is built
for slow, roughly linear Gauges — disk usage, queue backlog — where "will this cross a threshold
soon" matters more than the exact current value:
# Will free disk space go negative within the next 4 hours, based on the last 6h trend?
predict_linear(node_filesystem_avail_bytes{mountpoint="/"}[6h], 4 * 3600) < 0
# https://prometheus.io/docs/prometheus/latest/querying/functions/
The range ([6h] above) controls how much history the regression is fit against — long enough to
smooth out noise, short enough to still reflect the current trend rather than a stale one from days
ago. predict_linear() assumes a straight-line trend; a workload with strong daily seasonality (a
disk that fills on business days and drains overnight) is a poor fit and will over- or under-predict
depending on where in the cycle the range window lands. This expression is a common alerting-rule
body in Recording & Alerting Rules — "page before it actually runs out" rather than "page once it already has."
label_replace and label_join: reshaping labels with regexes
Both functions return the input vector with one label added or overwritten, without touching sample
values — useful for normalizing labels that differ across jobs before an aggregation or a join-style
vector match (see PromQL: Operators & Aggregation).
label_replace(v, dst, replacement, src, regex) matches regex (a fully anchored RE2 pattern)
against the src label’s value and writes the result of substituting capture groups ($1, $2, …)
into replacement, storing it in dst:
# Pull the numeric instance id out of a pod name like "api-7" into its own label.
label_replace(
up{job="api"},
"instance_id", "$1",
"pod", "api-(.*)"
)
# https://prometheus.io/docs/prometheus/latest/querying/functions/
Every series where pod matches api-(.*) gains an instance_id label holding whatever the capture
group matched ("7" for pod="api-7"); a series that does not match regex at all is passed through
with dst left unset. This is the standard way to align label names that differ only in spelling
between two metrics before matching them together with on(…)/ignoring(…), and it is exactly
what Service Discovery & Relabeling
does at scrape time with relabel_configs — label_replace() is the PromQL-query-time equivalent
of that same regex-capture-and-rewrite idiom, applied per query instead of once at scrape.
label_join(v, dst, separator, src_label_1, src_label_2, …) concatenates one or more existing
labels into dst with separator between them — no regex, just a join:
# Build a single "namespace/pod" identity label out of two existing labels.
label_join(kube_pod_info, "pod_identity", "/", "namespace", "pod")
Both functions are pure-vector transforms with no aggregation, so they compose directly with
sum by (…), on(…) joins, or another label_replace() call in the same expression.
Time and date functions
A small family of functions ignores its (optional) vector argument entirely and instead returns the evaluation timestamp itself, broken down however is useful — most often to gate a rule to a particular window or day:
time() # evaluation time as a Unix timestamp, seconds
day_of_week(vector(time())) # 0 (Sunday) .. 6 (Saturday), in UTC
day_of_month(vector(time())) # 1..31
hour(vector(time())) # 0..23, UTC
minute(vector(time())) # 0..59
month(vector(time())) # 1..12
year(vector(time())) # e.g. 2026
# https://prometheus.io/docs/prometheus/latest/querying/functions/
vector(time()) turns the scalar time() into a one-sample instant vector so the other functions
(which expect a vector argument, defaulting to the current time when omitted) have something to
operate on; calling hour() with no argument is equivalent and more common in practice. A frequent
use is silencing a noisy rule outside business hours directly in the expression:
# Only fire during weekday business hours (UTC): Mon-Fri, 08:00-18:00.
(
rate(http_requests_total{status=~"5.."}[5m]) > 0.05
)
and on() (day_of_week() >= 1 and day_of_week() <= 5)
and on() (hour() >= 8 and hour() < 18)
These functions are all evaluated in UTC; converting to a local timezone for a display label (rather than a threshold) is normally left to Grafana, covered in Grafana: Dashboards & Visualization, rather than done inside PromQL.
Where these land in real rules
Every function above shows up, more or less verbatim, once queries stop being typed ad hoc into the
Prometheus expression browser and get promoted to recording rules (precomputing an expensive
histogram_quantile(sum(rate(…)))) or alerting rules (a predict_linear() or counter-reset-aware
rate() threshold). See Recording &
Alerting Rules for that promotion, and Prometheus & Monitoring for the rest of this
section.