Loki Log Labels & LogQL Reference

LogQL stream selectors, filter expressions and metric query functions.

Searchable LogQL reference covering label matchers, line filters, label parsers, format expressions and metric queries for Grafana Loki. Build correct log queries fast. It runs free in your browser on Gera Tools, with nothing uploaded.

Last updated Source: Gera Tools

What is a stream selector in LogQL?

A stream selector is the required first part of every LogQL query, written in braces, that picks which log streams to read by matching labels, e.g. {app="api", env="prod"}. At least one label matcher with the = operator is required so Loki can locate the streams.

LogQL reference for Grafana Loki

LogQL is the query language for Grafana Loki. Every query starts with a stream selector (label matchers in braces), optionally followed by line filters, parsers that extract structured fields, label filters, and format stages. Wrapping a log query in a range function turns it into a metric query. This reference lists the operators and functions for each stage.

Filter the entries below by keyword or stage to find the right syntax.

How it works

Loki indexes only labels, not log content. A query first uses the brace selector to locate matching streams — combinations of label values — using the inverted index; this is the cheap, indexed step and at least one = matcher is required. The selected log lines then flow through a pipeline of stages.

Line filters (|=, !=, |~, !~) match against the raw line. Parsers (| json, | logfmt, | regexp, | pattern) extract fields into temporary labels you can then filter (| status="500") or reformat (| line_format, | label_format). For metrics, range aggregations such as rate(...[5m]) and count_over_time(...[1h]) convert matched lines into a time series, which you then aggregate with sum, avg, or topk exactly as in PromQL. Keeping label cardinality low is essential because each label-value combination is a separate stream.

LogQL pipeline stages at a glance

StagePurposeExample
Stream selectorPick streams by indexed labels{app="api", env="prod"}
Line filterMatch raw text in selected lines|= "error", !~ "healthcheck"
ParserExtract fields from structured lines| json, | logfmt, | regexp
Label filterFilter on extracted fields| status >= 500
FormatReshape the log line or labels| line_format, | label_format
Metric queryCount or rate lines into a time seriesrate(...[5m]), count_over_time(...[1h])
AggregationPromQL-style sum/avg/topksum by (route)(...)

Worked examples

Find errors in a service and count them per minute:

{app="checkout", env="prod"} |= "error"
sum by (level) (
  rate({app="checkout"} | json | __error__="" [1m])
)

Extract a latency field and chart its 95th percentile:

quantile_over_time(0.95,
  {app="api"} | json | unwrap duration_ms [5m]) by (route)

Count HTTP 5xx responses by route over 15-minute windows:

sum by (route) (
  count_over_time(
    {app="gateway"} | json | status >= 500 [15m]
  )
)

Performance and cardinality rules

Put line filters before parsers. A filter like |= "error" discards non-matching lines before the parser has to process them. Parsing JSON is relatively expensive; filtering first reduces that work substantially.

Keep label cardinality low. Each unique set of label values is a separate stream with its own index entry and chunk file. Putting high-cardinality values (user IDs, request UUIDs, IP addresses) into labels explodes storage and slows queries. Instead, keep those values in the log line body and extract them at query time with | regexp or | json.

Use at least one equality matcher. Loki requires at least one = label matcher in the stream selector so it can use the index. A selector with only != or regex matchers forces a full scan and is rejected by default in production clusters.

The __error__ label. When a parser fails to parse a line (for example, non-JSON in a JSON stream), Loki sets the __error__ and __error_details__ labels instead of dropping the line. Filtering | __error__="" keeps only lines that parsed cleanly; filtering | __error__!="" surfaces parse failures for debugging.

Tips and notes

Put line filters before parsers so cheap text matching reduces the lines a parser must process. Avoid high-cardinality labels in the selector; extract IDs at query time with a parser instead. When building metric dashboards in Grafana, prefer rate(...) for per-second rates and sum by (label)(count_over_time(...)) for total event counts broken down by dimension.