Files
magnus919_agent-skills/telemetry/references/05-query-workflows.md
T
Magnus HedemarkandGitHub fc246eb079 docs(telemetry): deepen bounded PromQL and LogQL workflows
Squash-merge verified telemetry query depth at exact head 02d16c58a4. Required validate and paired evaluation checks passed; advisory droid review had no blocking findings.
2026-09-01 20:06:09 -04:00

72 lines
5.5 KiB
Markdown

# PromQL and LogQL Query Workflows
> **Last Updated:** 2026-09-01
Use this reference for an offline query review or a live, read-only query investigation. It covers query shape and evidence; it does not replace `promtool` or Loki's parser.
## 1. Define a bounded question
Write down the signal, service/job, environment, exact UTC start and end, resolution, and expected unit before writing the expression. Prefer a narrow selector with equality matchers:
```promql
sum by (job, route) (rate(http_requests_total{job="api", environment="prod"}[5m]))
```
For logs, select streams first and filter lines second:
```logql
{app="api", environment="prod"} |= "timeout" | json | duration_ms > 1000
```
Do not begin with `{}` or `metric{label=~".*"}`. Avoid unbounded regex, arbitrary joins, and multi-day ranges while exploring. A range query must state its step; choose a step no finer than the evidence needs. Bound concurrency and request timeouts at the client/query gateway.
## 2. Validate in two distinct passes
Syntax and semantics are different claims:
1. **Syntax:** parse the expression with `promtool check rules` (PromQL) or the target Loki/Grafana parser. Check balanced delimiters, operators, durations, and LogQL pipeline syntax. A HTTP 200 only proves the endpoint accepted the request, not that the question is correct.
2. **Semantics:** confirm metric/log names, label keys and value types, counter-versus-gauge intent, compatible label sets for binary operators, and aggregation dimensions. For joins, make the matching labels explicit (`on(...)`/`ignoring(...)`) and use `group_left`/`group_right` only when the cardinality relationship is known. For LogQL, verify parsed fields exist and that aggregation is applied to the intended streams.
Record both results separately. A syntactically valid query can return an empty vector because the label was renamed, the metric was never scraped, or retention has expired.
## 3. Capture bounded evidence
For every query record:
- backend and endpoint, HTTP status, and response `status`/error type;
- exact query text or a redacted query identifier;
- UTC `start`, `end`, and Prometheus `step` (or Loki limit/direction);
- selector and grouping labels, estimated series/stream count, and duration;
- whether the result is instant, range, logs, or a derived metric;
- parser result, semantic checks, warnings, and a link/request ID if available.
Use instant queries to test existence, then a short range query to establish behavior. Keep result samples bounded; do not paste raw logs or credentials.
## 4. Control cost and cardinality
Start with a narrow equality selector and a short range. Expand one dimension at a time, checking series count and latency after each change. Prefer recording rules for repeatedly used expensive PromQL expressions and pre-extracted low-cardinality Loki labels for common filters. Keep request limits explicit: maximum lookback, step floor, series/stream limit, bytes/line limit, timeout, and query concurrency. A gateway or tenant limit is a safety net, not permission to issue a broad query.
Avoid `count by` over unbounded labels, grouping by request/user/trace IDs, regex over all streams, JSON/regexp parsing before a selective matcher, and joins where both sides are high cardinality. A label whose value changes per event belongs in the log body or structured metadata, not Loki's index.
## 5. Diagnose empty and partial results without inventing zeros
An empty result is **unknown**, never numeric zero. Distinguish:
| Observation | Next read-only check | Interpretation |
|---|---|---|
| Query parser error / non-2xx | response error type and expression | Query error; fix syntax or request shape |
| Empty with healthy backend | instant existence query, label/series API, exact time window | Could be absent label, wrong selector, or no events |
| Empty only for old time | retention bounds and backend clock | Data expired or outside retention |
| Empty for one target | scrape/ingest target state and relabel output | Failed target, dropped series, or missing stream |
| Stale/flat value | sample timestamps, scrape freshness, exporter metrics | Stale data, stalled producer, or timestamp issue |
| Partial response/warnings | HTTP status, response warnings, shard/limit metrics | Incomplete evidence; do not aggregate as complete |
| Backend timeout/limit | query duration, series/stream limit, concurrency | Cost or capacity rejection; narrow query |
For ratios, do not substitute zero for a missing numerator or denominator. Report `no data`, preserve `NaN`/absence semantics, and state what evidence is missing. A target can be `up` while a particular metric is absent because relabeling or instrumentation changed.
## Ownership and deferral
Route SLI/SLO definitions, error budgets, alert thresholds, and paging strategy to [platform-engineering](../../platform-engineering/SKILL.md). Route Grafana panels, data-source configuration, Grafana alert rules, contact points, and notification policies to [grafana](../../grafana/SKILL.md). This skill supplies backend query evidence those owners consume.
Tempo/tracing query workflows are intentionally deferred to a future named-tool decision. This skill may preserve trace/span IDs for correlation in logs and metrics, but it does not claim Tempo commands, APIs, or trace-query semantics. Route application propagation to [backend-engineering](../../backend-engineering/SKILL.md) and revisit a dedicated Tempo skill only when a concrete operational surface and non-overlapping trigger are established.