Perses Query Builder
Build and optimize queries for Perses dashboard panels.
Operator Context
This skill constructs, validates, and optimizes queries embedded in Perses panel definitions. It handles PromQL (Prometheus), LogQL (Loki), and TraceQL (Tempo) with correct variable interpolation and datasource binding.
Hardcoded Behaviors (Always Apply)
- Variable-aware: Always use Perses variable syntax or — never hardcode label values that should come from variables
- Datasource-scoped: Every query MUST reference its datasource by both and fields
- Interpolation-correct: Use for matchers, or for multi-select labels — never use bare with regex operators
- Rate interval alignment: Use when the platform provides it; otherwise set rate intervals >= 4x the scrape interval
Default Behaviors (ON unless disabled)
- PromQL default: Default to PrometheusTimeSeriesQuery if query type not specified
- Optimization suggestions: Flag recording rule candidates for expensive aggregations over high-cardinality metrics
- Label matcher validation: Warn when queries lack a narrowing label matcher (e.g., selecting all series for a metric)
- Multi-value detection: When a variable is marked , automatically apply the correct interpolation format
Optional Behaviors (OFF unless enabled)
- Recording rule generation: Produce for identified candidates
- TraceQL exemplar linking: Add exemplar query alongside PromQL for trace correlation
- Query explain mode: Annotate each query clause with comments explaining what it selects
What This Skill CAN Do
- Build PromQL, LogQL, and TraceQL queries for Perses panel specs
- Apply correct Perses variable interpolation formats (, , etc.)
- Validate query syntax and flag common PromQL/LogQL/TraceQL errors
- Suggest query optimizations (recording rules, label narrowing, rate intervals)
- Wire queries to the correct datasource kind and name
What This Skill CANNOT Do
- Create or configure datasources (use perses-datasource-manage)
- Build full dashboards or panel layouts (use perses-dashboard-create)
- Deploy Perses server instances (use perses-deploy)
- Develop custom Perses plugins (use perses-plugin-create)
Error Handling
PromQL Syntax Errors
Symptom: Query fails validation — missing closing bracket, invalid function name, bad label matcher syntax.
Detection: Look for unbalanced
,
,
; unknown function names;
with unescaped special chars.
Resolution: Fix the syntax. Common fixes:
- Add missing closing or
- Replace value with a valid RE2 regex (no lookaheads)
- Use correct function name (e.g., not , not )
Variable Interpolation Format Mismatch
Symptom: Dashboard renders wrong results or query errors when multi-value variable is selected.
Detection:
or
used with
matcher;
used with
(needs regex format).
Resolution:
- For matchers: use (produces )
- For with multi-select: use or depending on downstream expectation
- For JSON API params: use
Datasource Kind Mismatch
Symptom: Query silently returns no data or errors at runtime with "unsupported query type".
Detection: Query plugin
does not match datasource
(e.g.,
PrometheusTimeSeriesQuery
referencing a
).
Resolution: Align the query plugin kind with the datasource kind:
PrometheusTimeSeriesQuery
→
- →
- →
High-Cardinality Query Warnings
Symptom: Query is slow, times out, or overwhelms Prometheus.
Detection: No label matchers narrowing selection;
missing or with no interval; aggregation over unbounded label set.
Resolution:
- Add label matchers to reduce selected series (at minimum or )
- Wrap counters in or with an appropriate interval
- Consider a recording rule for expensive or multi-level aggregations
Anti-Patterns
Hardcoding Label Values
Wrong:
http_requests_total{namespace="production"}
in a panel query.
Right:
http_requests_total{namespace="$namespace"}
using a dashboard variable.
Why: Hardcoded values break reusability across environments and defeat the purpose of dashboard variables.
Bare with Multi-Value or Regex
Wrong:
http_requests_total{pod=~"$pod"}
when
is a multi-select variable.
Right:
http_requests_total{pod=~"${pod:regex}"}
.
Why: Without
format, multi-select values are not joined with
— the query matches only the first selected value or produces a syntax error.
Missing Datasource Spec in Query
Wrong: Omitting the
block or specifying only
without
.
Right:
yaml
datasource:
kind: PrometheusDatasource
name: prometheus
Why: Perses needs both
and
to resolve the datasource. Omitting
causes runtime resolution failures.
Using Without Meaningful Interval
Wrong:
rate(http_requests_total{job="api"}[1s])
.
Right:
rate(http_requests_total{job="api"}[$__rate_interval])
or
aligned with scrape interval.
Why: Intervals shorter than the scrape interval produce empty results;
auto-adapts.
Anti-Rationalization
| Rationalization | Reality | Required Action |
|---|
| "Bare works fine for single-select" | Variables can be changed to multi-select later, breaking the query | Always use explicit format when combined with |
| "Datasource kind is obvious from context" | Perses resolves datasources by kind+name pair at runtime | Always specify both and |
| "This query is simple enough to skip validation" | Simple queries with typos still fail silently | Validate every query against syntax rules |
| "Recording rules are premature optimization" | over thousands of series will time out in production | Flag recording rule candidates for expensive aggregations |
FORBIDDEN Patterns
- NEVER use with (equality) matchers — regex format with causes silent mismatches
- NEVER omit from the datasource reference — Perses cannot resolve by name alone
- NEVER mix query plugin types within a single panel query list (e.g., PromQL and TraceQL in the same array)
- NEVER use Grafana-style or — Perses uses (no braces, double underscores)
- NEVER assume a variable supports multi-select — check the variable definition's field
Blocker Criteria
Do NOT proceed past the BUILD phase if any of these are true:
- Datasource unknown: The target datasource name and kind have not been confirmed — query cannot be validated
- Variable definitions missing: Query references but no matching variable exists in the dashboard spec
- Query type ambiguous: Cannot determine whether PromQL, LogQL, or TraceQL is needed from user request
- Metric name unverified: The metric name referenced does not exist in the target Prometheus/Loki/Tempo instance and the user has not confirmed it
Instructions
Phase 1: IDENTIFY
Goal: Determine query type, datasource, and variable context.
- Query type: Identify which query language is needed:
- PrometheusTimeSeriesQuery (PromQL) — metrics, counters, histograms
- TempoTraceQuery (TraceQL) — distributed traces
- LokiLogQuery (LogQL) — log streams
- Datasource: Confirm the datasource and from the dashboard or project context
- Variables: Identify which dashboard variables the query should reference and their setting
Gate: Query type, datasource, and variable context confirmed. Proceed to Phase 2.
Phase 2: BUILD
Goal: Construct the query with proper variable templating and datasource binding.
yaml
queries:
- kind: TimeSeriesQuery
spec:
plugin:
kind: PrometheusTimeSeriesQuery
spec:
query: "rate(http_requests_total{job=\"$job\", instance=~\"${instance:regex}\"}[$__rate_interval])"
datasource:
kind: PrometheusDatasource
name: prometheus
Variable interpolation reference:
| Format | Output | Use With |
|---|
| | matchers |
| | API params, |
| | Custom pipe-delimited contexts |
| | JSON payloads |
| | Quoted lists |
| | Quoted lists |
| | Glob patterns |
| | Lucene queries |
| (first only) | Single-value forced |
| | URL-encoded params |
${var:singlevariablevalue}
| | Force single value |
Gate: Query built with correct interpolation and datasource. Proceed to Phase 3.
Phase 3: OPTIMIZE
Goal: Review the query for performance and correctness.
- Label narrowing: Ensure at least one selective label matcher is present (e.g., , )
- Rate intervals: Confirm / intervals align with scrape interval or use
- Recording rule candidates: Flag over high-cardinality metrics, multi-level aggregations, or any query aggregating over > 1000 estimated series
- Variable format audit: Verify every reference uses the correct interpolation format for its operator context
- Datasource alignment: Confirm query plugin kind matches datasource kind
Gate: Query optimized and validated. Task complete.
References
- Perses Variable Interpolation — Official docs on variable formats
- Perses Panel Queries — Query spec structure
- PromQL Docs — PromQL syntax reference
- Perses Datasource Config — Datasource kind/name binding
- Recording Rules Best Practices — When to create recording rules