{app="nginx"}Basic stream selector. Selects all log streams where the `app` label equals "nginx". Every LogQL expression must start with a stream selector in curly braces.
{app="nginx"}{job="varlogs"}{namespace="production"}Grafana Loki cheat sheet — 80+ entries covering LogQL stream selectors, line filters, parsers, metric queries, aggregations, Promtail stages, Loki config, and HTTP API.
{app="nginx"}Basic stream selector. Selects all log streams where the `app` label equals "nginx". Every LogQL expression must start with a stream selector in curly braces.
{app="nginx"}{job="varlogs"}{namespace="production"}{app="nginx", env="prod"}Multiple label conditions are ANDed together. Only streams where BOTH `app="nginx"` AND `env="prod"` match are selected. There is no OR at the stream selector level — use regex `=~` for that.
{app="nginx", env="prod"}{job="nginx", namespace="prod", pod=~"api-.*"}{app=~"api|frontend"}RE2 regex label matcher. Matches streams where `app` equals "api" OR "frontend". The regex is anchored at both ends — `"api"` only matches the exact string; use `"api.*"` to match any prefix.
⚠ Gotcha: Regex matchers scan all known streams rather than using an index lookup. Prefer exact `=` matchers for high-cardinality labels like `pod` or `container_id`.
{app=~"api|frontend|backend"}{namespace=~"prod|staging"}{pod=~"api-[a-z]+-[0-9a-f]+"}{app!="nginx"}Negative equality matcher. Selects all streams where `app` does NOT equal "nginx". Also matches streams where the label is absent.
{job="prod", env!="dev"}{namespace!="kube-system"}{app!~"dev|test"}Negative regex matcher. Selects streams where `app` does NOT match the regex. Useful for excluding test or dev environments in queries that should only run against production.
{env!~"dev|staging|test"}{namespace!~"kube-.*|istio-.*"}{__filename__=~"/var/log/nginx.*"}Internal metadata labels start with double underscores. `__filename__` is set by Promtail for file-based scrapers. Other common internal labels: `__path__` and `__journal_SYSTEMD_UNIT__` for journald.
{__filename__=~"/var/log/nginx/.*\.log"}{__journal_SYSTEMD_UNIT__="nginx.service"}{app=~".+"}Match all streams that have the `app` label set (to any non-empty value). Useful to avoid implicit cardinality explosion when you actually mean "all services" in a metric query.
⚠ Gotcha: An empty selector `{}` in a metric query hits EVERY stream in Loki. Always include at least one label filter for production queries.
sum(rate({app=~".+"} [5m])) by (app){namespace=~".+", env="prod"}{app="api"} offset 1hTime offset modifier. Shifts the query window back in time. Useful for comparing current metrics against the same window from an hour ago: `rate({app="api"} [5m]) / rate({app="api"} [5m] offset 1h)`.
rate({app="api"} [5m] offset 1h)rate({app="api"} |= "error" [5m]) / rate({app="api"} |= "error" [5m] offset 1d)|= "error"Line contains filter. Keeps only log lines that contain the literal string "error". Case-sensitive. Applied before any parsing — the fastest way to reduce log volume.
⚠ Gotcha: `|=` is case-sensitive. "Error" and "error" are different. Use `|~ "(?i)error"` for case-insensitive matching.
{app="nginx"} |= "error"{app="api"} |= "ERROR" |= "timeout"!= "debug"Line does not contain filter. Discards log lines that contain the literal string. Useful for filtering out verbose log levels before parsing, which avoids parser cost on dropped lines.
{app="api"} != "debug" != "trace"{namespace="prod"} != "health check" != "heartbeat" != "ping"|~ "ERR|error|WARN"Regex line filter. Keeps lines matching the RE2 pattern. Use alternation (`|`) to match multiple keywords in one filter. Unlike stream selectors, line filter regex is NOT anchored at both ends.
{app="nginx"} |~ "5[0-9]{2}"{app="api"} |~ "(?i)(error|fatal|panic)"{app="worker"} |~ "^[0-9]{4}-[0-9]{2}-[0-9]{2}.*ERROR"!~ "debug|trace"Regex not-match line filter. Discards lines matching the RE2 pattern. Chain multiple filters to narrow down results — each stage further reduces the line count.
{app="api"} |= "request" !~ "health|ping|metrics"{job="nginx"} !~ "2[0-9]{2}" |= "GET"|= "error" |= "timeout"Chained line filters are ANDed. A line must pass ALL filters to continue down the pipeline. Filters execute left-to-right; early exits drop lines cheaply before reaching expensive stages.
{app="api"} |= "error" |= "connection" != "debug"{app="db"} |~ "deadlock|timeout" != "warning"|> "<_> level=<level> msg=<msg> <_>"Pattern line filter (Loki 3.x+). Named placeholders `<field>` match any non-whitespace value; `<_>` matches any content. Faster than regex for structured log lines. Does NOT extract labels — use a parser stage after for that.
{app="api"} |> "<_> method=<method> path=<path> status=<status> <_>"|= `"error": true`Backtick-delimited strings avoid backslash escaping. Use backticks when your filter literal contains double quotes, backslashes, or any character that would need escaping in a regular quoted string.
{app="api"} |= `"error": true`{app="nginx"} |= `GET "/api/v1/users"` |= `"status": 500`| jsonJSON parser. Extracts all top-level JSON keys as labels. Nested objects are flattened: `{"req":{"method":"GET"}}` produces `req_method="GET"`. Malformed lines get `__error__="JSONParserErr"`.
⚠ Gotcha: Do not use `| json` on non-JSON logs — every line becomes a parser error and inflates cardinality with `__error__` labels. Always add `| __error__ = ""` after to exclude failed parses from metrics.
{app="api"} | json{app="api"} |= "error" | json | __error__ = "" | level = "error"| json field="nested.key"JSON parser with explicit field paths. Use dotted notation for nested keys. The label name defaults to the last path component. Specify `alias=path` to rename: `| json status="response.code"`.
{app="api"} | json status="response.status", user="auth.userId"{app="api"} | json msg="message" | msg = "request completed"| logfmtLogfmt parser. Parses space-separated `key=value` or `key="value with spaces"` pairs — the Go `log/logfmt` format. Widely used in Go applications and Kubernetes system components.
{app="api"} | logfmt | level = "error"{job="kube-apiserver"} | logfmt | component = "etcd" | err != ""| pattern "<ip> - - [<_>] \"<method> <path> <_>\" <status> <bytes>"Pattern parser. Named placeholders capture values into labels; `<_>` discards matched content. Faster than regex and more readable for fixed-column formats like nginx/Apache access logs.
{job="nginx"} | pattern "<ip> - - [<_>] \"<method> <path> <_>\" <status> <size>"{job="nginx"} | pattern "<ip> - - [<_>] \"<method> <path> <_>\" <status> <size>" | status = "500"| regexp "(?P<method>\\w+) (?P<path>[^ ]+) HTTP"Regex parser with RE2 named capture groups. Each `(?P<name>...)` group becomes a label. Unnamed groups are discarded. Slowest parser option — prefer `| json`, `| logfmt`, or `| pattern` when possible.
⚠ Gotcha: `| regexp` is 5–10× slower than structured parsers. Only use it when the log format is too irregular for other parsers.
{job="app"} | regexp `(?P<level>\w+)\s+(?P<component>\[\w+\])\s+(?P<msg>.*)`| unpackUnpack parser. Reads log entries packed by the Promtail `pack` pipeline stage — a JSON envelope that encodes the original log line in `_entry` plus extra labels. Restores the original line for further processing.
{job="packed-logs"} | unpack{job="packed-logs"} | unpack | level = "error"| line_format "{{.level}} {{.msg}}"Rewrites the entire log line using a Go template. Reference extracted labels with `.label_name`. Available template functions: `ToUpper`, `ToLower`, `Replace`, `trim`, `upper`, `lower`. Useful for normalizing mixed log formats before display.
{app="api"} | json | line_format "{{.level | upper}} {{.ts}} {{.msg}}"{app="api"} | logfmt | line_format "[{{.level}}] {{.component}}: {{.msg}}"| label_format level=log_levelRenames labels in the pipeline. Right side is the source label, left side is the new name. To delete a label, set it to the empty string: `| label_format unwanted=""`. To rename and copy: both sides exist and the source is kept.
{app="api"} | json | label_format status_code=status{app="api"} | logfmt | label_format level=lvl, message=msg| decolorizeStrips ANSI color/SGR escape codes from log lines before further processing. Use before `| json` or `| pattern` when your app emits colored terminal output to a log file, which would otherwise break parsers.
{app="colorful-app"} | decolorize | json{app="cli-tool"} | decolorize | pattern "<level> <msg>"| drop status, envDrop labels from the pipeline to reduce cardinality. Dropped labels cannot be filtered on afterward. Use to remove high-cardinality parser outputs (e.g., raw request body) that you do not need for metrics.
{app="api"} | json | drop _entry, tenant_id{job="nginx"} | pattern "..." | drop __filename__| status = "200"Label equality filter (post-parser). Filters lines where the extracted label `status` equals the string "200". If the label does not exist on a line, that line is dropped.
{app="nginx"} | logfmt | level = "error"{app="api"} | json | env = "prod" | service = "payments"| status != "200"Label inequality filter. Keeps lines where the label EXISTS and does not equal the value. Lines where the label is absent are also dropped — use `| status != ""` to keep absent-label lines.
{app="nginx"} | json | status != "200"{app="api"} | logfmt | level != "info" | level != "debug"| status =~ "5.."Label regex filter. Keeps lines where the label value matches the RE2 regex. The regex is anchored at both ends — `"5.."` matches exactly three chars starting with 5.
{app="nginx"} | json | method =~ "POST|PUT|PATCH"{app="api"} | logfmt | level =~ "error|fatal|panic"| duration > 1sNumeric label filter with duration unit. Loki parses Go-style duration strings (1s, 500ms, 2.5m, 1h) and compares numerically. Supported units: ns, us (µs), ms, s, m, h.
{app="api"} | json | duration > 500ms{app="db"} | logfmt | query_time >= 2s | query_time < 30s| bytes > 1mbNumeric label filter with byte unit. Recognized units: b, kib, kb, mib, mb, gib, gb, tib, tb. Comparison operators: `>`, `>=`, `<`, `<=`, `=`, `!=`.
{app="storage"} | json | response_bytes > 1mb{job="nginx"} | pattern "... <status> <bytes>" | bytes > 1024| __error__ = ""Filter out parser errors. When a line fails parsing, Loki sets `__error__` to the error type (e.g. "JSONParserErr", "LogfmtParserErr"). `| __error__ = ""` keeps only successfully-parsed lines and is essential for accurate metrics.
⚠ Gotcha: Without this filter, metric queries over parsed labels silently include error lines. Error lines have no extracted labels and slip past label filters — causing undercounting.
{app="api"} | json | __error__ = "" | level = "error"{job="nginx"} | logfmt | __error__ = "" | status > 499| level = "error" | status > 400Chained label filters are ANDed. All conditions must be true. Order does not affect correctness, but placing cheaper or more-selective filters first improves performance.
{app="api"} | json | level = "error" | duration > 1s | service = "payments"rate({app="nginx"} [5m])Per-second log line rate over the last 5 minutes. The most common Loki metric query. Use it as a baseline for "how busy is this service?" and for SLO burn-rate alerts.
rate({app="nginx"} [5m])sum(rate({namespace="prod"} [5m])) by (app)count_over_time({app="nginx"} [5m])Total count of log lines in the 5-minute range. Returns a raw count (not per-second). Useful for computing totals, but the value spikes when you widen the range — use `rate()` for trends.
⚠ Gotcha: Using `count_over_time` in a Grafana dashboard with a time range selector causes jarring value jumps. Use `rate()` in dashboards.
count_over_time({app="nginx"} |= "error" [1h])sum(count_over_time({namespace="prod"} [1d])) by (app)bytes_over_time({app="nginx"} [5m])Total bytes of compressed log data ingested in the range. Useful for ingestion cost analysis and capacity planning.
sum(bytes_over_time({namespace="prod"} [1h])) by (app)bytes_rate({app="nginx"} [5m])Per-second bytes ingested, averaged over the range window. The byte equivalent of `rate()`. Use for monitoring ingestion throughput and detecting log storms.
sum(bytes_rate({namespace="prod"} [5m])) by (app)topk(5, sum(bytes_rate({} [5m])) by (job))rate({app} |= "error" [5m])Error log rate. Combines stream selector, line filter, and metric function. The full log pipeline runs before the metric is computed. Forms the basis for error-rate SLO alerts.
sum(rate({app=~"api|frontend"} |= "error" [5m])) by (app)rate({app="nginx"} | json | status =~ "5.." [5m])rate({app} | json | status > 499 [5m])Filtered metric with a parser and label filter. Extracts `status` from JSON logs, then counts only HTTP 5xx lines per second. Best practice: add `| __error__ = ""` before label filters.
sum(rate({app="api"} | json | __error__ = "" | status > 499 [5m])) by (path)rate({job="nginx"} | pattern "<_> <status> <_>" | status =~ "5.." [5m])topk(5, sum(rate({app} [5m])) by (pod))Top 5 pods by log rate. Aggregation operators (`sum`, `topk`, `avg`, etc.) wrap metric expressions. Evaluation order: log pipeline → metric function → aggregation operator.
topk(10, sum(rate({namespace="prod"} [5m])) by (app))bottomk(3, sum(bytes_rate({} [5m])) by (namespace))increase({app} |= "error" [1h])Alias for `count_over_time`. Returns the total log count increase over the range. Semantically clearer than `count_over_time` when you want "how many errors happened in the last hour".
increase({app="worker"} |= "job failed" [24h])sum(increase({namespace="prod"} |= "panic" [1h])) by (app)sum(rate({app} [5m])) by (namespace)Sum metric values grouped by `namespace`. The `by (label, ...)` clause specifies which labels survive aggregation — all others are dropped. Metrics from all matching streams are summed.
sum(rate({} [5m])) by (app, namespace)sum(bytes_rate({namespace="prod"} [5m])) by (app)avg(rate({app} [5m])) by (env)Average metric value across streams sharing the same `env` label. Useful for normalizing rates when instances have different traffic volumes.
avg(rate({namespace="prod"} [5m])) by (app)max(rate({app} [5m])) by (host)Maximum metric value across streams, grouped by `host`. Useful for finding the busiest pod or node in a cluster.
max(rate({namespace="prod"} [5m])) by (pod)topk(10, sum(rate({app} [5m])) by (app))Return the top-k label sets by their aggregated value. Commonly used to rank services by log rate, error rate, or log volume.
topk(5, sum(rate({namespace="prod"} |= "error" [5m])) by (app))bottomk(3, sum(rate({app} [5m])) by (host))Return the bottom-k label sets by value. The inverse of `topk`. Useful for finding underutilized or silently failing services.
bottomk(5, sum(bytes_rate({namespace="prod"} [5m])) by (app))sum without (pod) (rate({app} [5m]))`without (labels)` aggregates over ALL labels EXCEPT those listed — the complement of `by`. Use when you know which dimensions to drop rather than which to keep.
sum without (pod, instance) (rate({namespace="prod"} [5m]))count(rate({app} [5m])) by (app)Count the number of distinct label sets (streams) contributing to the result, grouped by `app`. Useful for tracking how many active pods or instances are logging.
count(rate({namespace="prod"} [5m])) by (app)| unwrap fieldExtracts a numeric value from label `field` to use as the sample value in over-time aggregation functions. Required before `avg_over_time`, `quantile_over_time`, etc. The label value must be a bare number.
⚠ Gotcha: For duration strings (e.g., "200ms") use `| unwrap duration(field)`. For byte strings (e.g., "1.5MiB") use `| unwrap bytes(field)`. Plain `unwrap` only parses bare numbers.
{app="api"} | json | unwrap response_ms{app="api"} | logfmt | unwrap duration(request_time){app="storage"} | json | unwrap bytes(response_size)quantile_over_time(0.95, {app} | json | unwrap duration_ms [5m]) by (path)P95 latency per endpoint. The Loki equivalent of Prometheus `histogram_quantile`. Combine: stream selector → parser → `| unwrap` → `quantile_over_time` → optional `by` aggregation.
quantile_over_time(0.50, {app="api"} | json | unwrap duration_ms [5m]) by (path)quantile_over_time(0.99, {app="api"} | logfmt | unwrap duration(resp_time) [5m]) by (endpoint)avg_over_time({app} | json | unwrap response_ms [5m])Mean of the unwrapped numeric values over the time range. Use for average latency, average queue depth, or any arithmetic mean extracted from log lines.
avg_over_time({app="api"} | logfmt | unwrap duration(resp_time) [5m]) by (service)max_over_time({app} | json | unwrap bytes [5m])Maximum unwrapped value in the time range. Useful for peak response size, maximum latency spike, or worst-case memory usage from log data.
max_over_time({app="api"} | json | unwrap duration_ms [5m]) by (path)min_over_time({app} | json | unwrap bytes [5m])Minimum unwrapped value in the range. Use together with `max_over_time` to compute the observed value range (spread) from log data.
min_over_time({app="cache"} | json | unwrap hit_rate [5m])sum_over_time({app} | json | unwrap bytes_transferred [5m])Sum of all unwrapped values over the range. Useful for total bytes transferred, total events counted, or any cumulative metric embedded in log lines.
sum_over_time({app="gateway"} | logfmt | unwrap bytes(transferred) [1h])first_over_time / last_over_time`first_over_time` returns the oldest sample in the range; `last_over_time` returns the newest. Useful for "what was the status code at the start/end of this window?" from log data.
first_over_time({app="api"} | json | unwrap status [5m]) by (host)last_over_time({app="api"} | json | unwrap queue_size [1m])absent_over_time({app="cron"} [10m])Returns a vector with value 1 if no log lines arrived from the matching stream for the ENTIRE range. Returns empty when logs exist. The standard pattern for alerting on silent cron jobs or dead services.
⚠ Gotcha: The stream selector must include at least one label matcher, otherwise Loki cannot identify which stream is "absent" from the result.
absent_over_time({app="backup-job", env="prod"} [30m])absent_over_time({job="heartbeat", host="server-01"} [5m])vector(0)Returns a constant scalar as a single-element vector. Useful in arithmetic: `rate({app} [5m]) / vector(100)` for percentages, or `sum(rate) or vector(0)` to ensure a zero baseline when the stream is absent.
sum(rate({app="api"} |= "error" [5m])) / sum(rate({app="api"} [5m])) * vector(100)label_replace(rate({app} [5m]), "short_host", "$1", "host", "(.*)\\..*")Post-aggregation label manipulation. `label_replace(expr, dst, replacement, src, regex)` applies a regex to the `src` label and writes the result to `dst`. Useful for normalizing long hostnames into short names.
label_replace(sum(rate({app} [5m])) by (host), "short", "$1", "host", "([^.]+).*")pipeline_stages: (overview)Promtail pipeline stages process each log line before sending to Loki. Stages run in order: `regex`/`json` to extract, `labels` to promote to Loki stream labels, `timestamp` to override time, `output` to rewrite the log line.
pipeline_stages:
- regex:
expression: '(?P<level>INFO|WARN|ERROR|DEBUG)'
- labels:
level:
- timestamp:
source: time
format: RFC3339regex stageExtracts named groups from the log line using a Go RE2 regex. Captured values are stored in the extracted map, available for subsequent stages. `source` defaults to the raw log line.
- regex:
expression: '^(?P<ip>\S+) \S+ \S+ \[(?P<ts>[^\]]+)\] "(?P<method>\w+) (?P<path>\S+)[^"]*" (?P<status>\d{3}) (?P<bytes>\d+)'- regex:
source: message
expression: 'error: (?P<error_code>[A-Z0-9_]+)'json stageParses the log line (or `source` label) as JSON and extracts specified keys into the extracted map. Use `expressions` to rename during extraction.
- json:
expressions:
level: level
msg: message
ts: timestamp- json:
source: attributes
expressions:
user_id: userIdlabels stagePromotes values from the extracted map into Loki stream labels. Labels increase index cardinality — only promote low-cardinality values (e.g., `level`, `app`, `env`) that you will filter on in LogQL.
⚠ Gotcha: Never promote high-cardinality values (user ID, request ID, IP address) as labels — this inflates the index and causes out-of-memory errors on the ingester.
- labels:
level:
app:
env:- labels:
level: # source key from extracted map
component: # same name, same keytimestamp stageOverrides the log entry timestamp from an extracted field. Supports Go `time.Parse` layouts, `Unix`, `UnixMs`, `UnixUs`, `UnixNs`, and common names (`RFC3339`, `RFC3339Nano`).
- timestamp:
source: ts
format: RFC3339- timestamp:
source: log_time
format: "2006-01-02T15:04:05.000Z07:00"
fallback_formats: ["2006-01-02 15:04:05"]multiline stageMerges multi-line log entries (e.g., Java stack traces) into a single Loki log line. `firstline` is a regex that matches the start of a new entry; subsequent lines are appended until the next match.
- multiline:
firstline: '^[0-9]{4}-[0-9]{2}-[0-9]{2}'
max_wait_time: 3s
max_lines: 128output stageSets the log line that is sent to Loki to the value of a key in the extracted map. Use it to send a structured field as the log body instead of the raw line (e.g., after parsing JSON).
- json:
expressions:
msg: message
- output:
source: msgpack stagePacks the log line and any labels in the extracted map into a JSON envelope: `{"_entry":"original line","key":"value"}`. Used before `| unpack` in Loki to restore the original line for advanced processing.
- pack:
labels:
- level
- componentauth_enabled: falseDisables multi-tenancy. All requests are treated as the anonymous `fake` tenant. Required for single-tenant deployments. For multi-tenant mode set to `true` and send `X-Scope-OrgID` header on every request.
auth_enabled: false
# multi-tenant header: # X-Scope-OrgID: my-org-id
server: { http_listen_port: 3100 }Loki listens on port 3100 (HTTP) and 9096 (gRPC) by default. The gRPC port is used for internal component communication in distributed/microservices mode.
server: http_listen_port: 3100 grpc_listen_port: 9096 log_level: info
ingester: wal + ringThe write-ahead log (WAL) protects against data loss on ingester crash. Enabled by default since Loki 2.4. Requires a persistent directory. The ring coordinates replication across ingester instances.
ingester:
wal:
enabled: true
dir: /loki/wal
lifecycler:
ring:
kvstore:
store: memberlist
replication_factor: 3schema_configDefines the object store, chunk format, and index type used in each time period. Multiple schema periods allow migrating to a newer schema without re-indexing historical data.
schema_config:
configs:
- from: 2020-10-24
store: boltdb-shipper
object_store: filesystem
schema: v11
index:
prefix: index_
period: 24h
- from: 2023-01-01
store: tsdb
object_store: s3
schema: v12
index:
prefix: loki_index_
period: 24hstorage_configConfigures chunk and index storage backends. Common choices: `filesystem` for local dev/single-node, `s3`/`gcs`/`azure` for production. The `boltdb_shipper` uploads local index files to object storage.
storage_config:
aws:
s3: s3://us-east-1
bucketnames: my-loki-chunks
boltdb_shipper:
active_index_directory: /loki/index
cache_location: /loki/cache
shared_store: s3limits_configPer-tenant ingestion and query limits. The defaults are conservative: `ingestion_rate_mb: 4` causes 429s in production. Tune for your workload. Set `retention_period` here or per-tenant via the ruler API.
⚠ Gotcha: The default `max_streams_per_user: 10000` is often hit in Kubernetes deployments with many pods. Increase or suppress noisy label dimensions first.
limits_config: ingestion_rate_mb: 16 ingestion_burst_size_mb: 32 max_streams_per_user: 100000 max_query_length: 721h retention_period: 30d
ruler: { enable_api: true }Enables the Loki built-in ruler for evaluating LogQL alerting rules (same as Prometheus rules but with LogQL in `expr`). Configure `alertmanager_url` to route firing alerts to Alertmanager.
ruler:
enable_api: true
enable_alertmanager_v2: true
alertmanager_url: http://alertmanager:9093
storage:
type: local
local:
directory: /loki/rulesLoki alerting rule YAMLLoki alerting rules use the standard Prometheus YAML format. The `expr` field takes a LogQL metric expression. `absent_over_time`, `rate`, and `sum(rate)` are commonly used in alert expressions.
groups:
- name: loki-alerts
rules:
- alert: HighErrorRate
expr: |
sum(rate({app="api"} |= "error" [5m])) by (env)
/ sum(rate({app="api"} [5m])) by (env) > 0.05
for: 5m
labels:
severity: warning
annotations:
summary: "Error rate > 5% for {{ $labels.env }}"
- alert: ServiceSilent
expr: absent_over_time({app="cron"} [30m])
for: 0m
labels:
severity: criticalcompactor: { retention_enabled: true }The compactor merges boltdb-shipper or tsdb index shards, applies retention policies, and deletes expired chunks. Must run as a singleton in cluster mode. Retention is enforced here, not in `limits_config` alone.
compactor: working_directory: /loki/compactor shared_store: s3 retention_enabled: true retention_delete_delay: 2h retention_delete_worker_count: 150
GET /loki/api/v1/queryInstant query. Evaluates a LogQL expression at a single point in time. Required: `query`. Optional: `time` (RFC3339 or Unix ns, default now), `limit` (max log lines), `direction` (forward/backward).
curl "http://localhost:3100/loki/api/v1/query?query=%7Bapp%3D%22nginx%22%7D&limit=50"
curl -G --data-urlencode 'query={app="nginx"} |= "error"' http://localhost:3100/loki/api/v1/queryGET /loki/api/v1/query_rangeRange query over a time window. Required: `query`, `start`, `end` (Unix ns or RFC3339), `step` (Prometheus duration or seconds). Returns a matrix of log streams or metric values.
curl "http://localhost:3100/loki/api/v1/query_range?query=%7Bapp%3D%22nginx%22%7D&start=1704067200000000000&end=1704153600000000000&step=60"
GET /loki/api/v1/labelsReturns all known label names within the default time window (last 6h). Optional: `start`/`end` to narrow the range, `query` (Loki 2.9+) to filter by stream selector.
curl http://localhost:3100/loki/api/v1/labels
curl -G --data-urlencode 'query={namespace="prod"}' http://localhost:3100/loki/api/v1/labelsGET /loki/api/v1/label/{name}/valuesReturns all known values for a label name. Essential for building dynamic LogQL selectors, Grafana variable dropdowns, and autocomplete. Optional: `start`/`end`, `query`.
curl http://localhost:3100/loki/api/v1/label/app/values
curl -G --data-urlencode 'query={namespace="prod"}' http://localhost:3100/loki/api/v1/label/app/valuesGET /loki/api/v1/seriesReturns all active label sets (streams) matching one or more selectors. Required: `match[]`. Useful for auditing which streams are actively ingesting and listing known label combinations.
curl -G --data-urlencode 'match[]={app="nginx"}' http://localhost:3100/loki/api/v1/seriescurl -G --data-urlencode 'match[]={namespace="prod"}' http://localhost:3100/loki/api/v1/seriesPOST /loki/api/v1/pushPush log entries to Loki. Body: JSON with `streams` array. Each stream has `stream` (label set object) and `values` (array of `["timestamp_ns_string", "log line"]` pairs). Timestamp must be a nanosecond Unix epoch as a string.
curl -X POST http://localhost:3100/loki/api/v1/push \
-H 'Content-Type: application/json' \
-d '{
"streams": [{
"stream": {"app": "myapp", "env": "prod"},
"values": [["'$(date +%s%N)'", "log message here"]]
}]
}'GET /loki/api/v1/tail (WebSocket)Live tail over WebSocket — streams new log lines matching the query as they are ingested. Required: `query`. Optional: `limit` (N historical lines to send on connect), `delay_for` (seconds of ingestion delay).
wscat -c "ws://localhost:3100/loki/api/v1/tail?query=%7Bapp%3D%22nginx%22%7D&limit=20"
GET /loki/api/v1/index/statsReturns ingestion statistics for a stream selector: total streams, chunks, entries, and bytes. Required: `query` (stream selector), `start`, `end`. Useful for capacity planning and cost attribution.
curl -G --data-urlencode 'query={namespace="prod"}' "http://localhost:3100/loki/api/v1/index/stats?start=1704067200000000000&end=1704153600000000000"GET /ready · GET /metrics · GET /config`/ready` returns HTTP 200 when all Loki components are ready (use as Kubernetes readiness probe). `/metrics` exposes Loki's own Prometheus metrics. `/config` shows the effective merged configuration (useful for debugging).
curl http://localhost:3100/ready
curl http://localhost:3100/metrics | grep loki_ingester
curl http://localhost:3100/config | less
Searchable Grafana Loki cheat sheet with 80+ entries across ten sections. Stream selectors: `{app="nginx"}`, label matchers `=` `!=` `=~` `!~`, combining multiple label conditions, internal metadata labels. Line filters: `|=` `!=` `|~` `!~` for fast pre-parse filtering, pattern filter `|>`, backtick strings. Parsers: `| json`, `| json field=…`, `| logfmt`, `| pattern`, `| regexp`, `| unpack`, `| decolorize`, `| line_format`, `| label_format`, `| drop`. Label filters: post-parser equality, regex, numeric comparisons with duration/bytes units, `__error__` handling. Metric queries: `rate`, `count_over_time`, `bytes_over_time`, `bytes_rate`, `increase`, filtered and aggregated forms. Aggregation: `sum`, `avg`, `max`, `topk`, `bottomk`, `without`. Functions: `unwrap`, `quantile_over_time`, `avg_over_time`, `absent_over_time`. Promtail: `regex`, `json`, `labels`, `timestamp`, `multiline`, `output`, `pack` pipeline stages. Loki config: `auth_enabled`, `schema_config`, `storage_config`, `limits_config`, `ruler`, alerting rules, `compactor`. HTTP API: `/loki/api/v1/query`, `/query_range`, `/labels`, `/push`, `/tail`. Every entry has bilingual text, copy-ready examples, and pitfall callouts. Search, category chips, one-click copy — all in-browser.
Paste or drop your content into the tool panel.
Click the button. All processing is local in your browser.
Copy the result or download to disk in one click.
Use it in the small gaps between coding, reviewing, debugging, and shipping.
These links move the current task into a more complete workflow.
It is 2am and an alert fires. You open the cheat sheet, grab `rate({app="api"} | json | status > 499 [5m])`, add `by (path)` to find the noisy endpoint, then copy `topk(5, sum(rate(…)) by (path))` to surface the worst offenders. No guessing the syntax at 2am — copy-paste in under two minutes.
Your service emits JSON logs with `"duration_ms"`. You need P95 per endpoint. Open the cheat sheet, copy `quantile_over_time(0.95, {app="api"} | json | unwrap duration_ms [5m]) by (path)`, and paste it into Grafana. The cheat sheet also shows you the `| unwrap duration(field)` variant for Go-style "200ms" duration strings — no trial-and-error on the syntax.
You need Promtail to extract `status`, `bytes`, and `method` from nginx access logs. The cheat sheet shows the exact `pipeline_stages` block with a `regex` stage, a `labels` stage to promote captures to Loki labels, and the common pitfall of forgetting to set `source: filename` in the `labels` stage. Copy the config, fill in your job name, and reload Promtail.
Using a broad stream selector `{job="varlogs"}` and filtering with `|= "error"` — always add app/service labels to the selector so Loki doesn't scan every log stream on the node.
Putting label filters BEFORE the parser — `{app="api"} | status = "error" | json` fails because `status` doesn't exist until after `| json`.
Using `count_over_time` in a dashboard for trends — it returns raw counts that spike when the range changes. Use `rate()` for trend graphs.
Forgetting `| __error__ = ""` after a parser — parser errors silently inflate your metrics because error lines have no extracted labels and thus slip through label filters.
Using `| regexp` when `| pattern` or `| json` would work — `| regexp` is 5–10x slower than structured parsers.
Everything runs in your browser. The cheat sheet is a static in-memory array and the search box, category chips, and copy button never make a network request. Nothing you type is logged or sent anywhere. Works offline, behind a corporate proxy, or on an air-gapped jump host.
Folks in your role tend to reach for these alongside this tool.