Grafana Loki is a log aggregation system built on one design decision that separates it from Splunk and Elasticsearch: it does not index the content of log lines, only a small set of labels attached to each stream. A stream is a unique combination of labels - say {app="checkout", env="prod", level="error"} - and Loki stores the raw log text for that stream as compressed chunks in object storage, keeping only the labels in its index. Because the index stays tiny, Loki runs far cheaper than systems that build a full inverted index over every word.
Labels, streams, and LogQL
You query Loki with LogQL, which mirrors Prometheus's PromQL by design. A query starts by selecting streams with a label matcher, then optionally filters the log text with line-filter expressions, and can aggregate matched lines into metrics. The label selector narrows the search to a handful of streams first; the line filter scans only those streams' chunks.
# Find error lines for the checkout service mentioning a timeout
{app="checkout", env="prod"} |= "ERROR" |= "timeout"
# Per-minute rate of 5xx responses, by host, as a metric
sum by (host) (
rate({app="checkout"} | json | status >= 500 [1m])
)
The cardinality trap
Loki's cheapness depends on keeping label cardinality low. Every distinct label-value combination creates a new stream, and putting high-cardinality values - request IDs, user IDs, full URLs - into labels explodes the stream count and destroys the performance advantage. The rule is to label by a few low-cardinality dimensions (service, environment, level, host) and keep everything high-cardinality inside the log line itself, where a line filter searches it at query time. Loki pairs naturally with Grafana for dashboards and with Prometheus for metrics, sharing the same label model across all three.
Frequently asked questions
Why is Loki cheaper than Elasticsearch or Splunk? It indexes only labels, not log content, so the index is small and raw logs live as compressed chunks in object storage. There is no full-text inverted index to build or store.
What is LogQL? Loki's query language, modeled on PromQL - select streams by label, filter lines by text, and optionally turn matched lines into metrics like rates and counts.
What is the cardinality trap? Putting high-cardinality values (request IDs, user IDs) into labels creates a separate stream per value, exploding stream count and ruining performance. Keep such values in the log line, not the labels.
How does Loki fit with the Grafana stack? It shares the Prometheus label model, so the same labels drive metrics in Prometheus, logs in Loki, and dashboards in Grafana.
See the monitoring & logging overview and compare with Datadog.