Generate label matchers, line filters, log aggregations, and metric queries in LogQL (Loki Query Language) following current standards and conventions. Use this skill when creating new LogQL queries, implementing log analysis dashboards, alerting rules, or troubleshooting with Loki.
Overall
score
93%
Does it follow best practices?
Validation for skill structure
CRITICAL: Always engage the user in collaborative planning before generating queries.
Gather requirements using AskUserQuestion:
job, namespace, app, level, service_name){job="app"}, line filters |= "error", label filters | status >= 500| json, | logfmt, | pattern "<ip> - <user>", | regexp "(?P<field>...)"count_over_time(), rate(), sum by (label), quantile_over_time()[5m], [1h], [24h]Before generating code, present a plain-English plan and confirm with the user via AskUserQuestion:
## LogQL Query Plan
**Goal**: [Description]
**Query Structure**:
1. Select streams: `{label="value"}`
2. Filter lines: [operations]
3. Parse logs: [parser]
4. Aggregate: [function]
**Does this match your intentions?**Once confirmed, MANDATORY: use the Read tool to consult the appropriate reference files before generating. Do NOT rely on prior knowledge or cached information.
| Query Complexity | File to Read |
|---|---|
| Complex aggregations (nested topk, multiple sum by, percentiles) | assets/common_queries.logql — verified patterns |
| Performance-critical queries (large time ranges, high-volume streams) | references/best_practices.md — sections #1-5, #15-18 |
| Alerting rules | references/best_practices.md — sections #19-21, #39 |
| Structured metadata / Loki 3.x features | references/best_practices.md — sections #35-37 |
| Template functions (line_format, label_format) | assets/common_queries.logql — Template Functions section |
| IP filtering, pattern extraction, regex | assets/common_queries.logql — exact syntax |
Paths to use with the Read tool:
Read(".claude/skills/logql-generator/assets/common_queries.logql") # Query patterns
Read(".claude/skills/logql-generator/references/best_practices.md") # Optimization and anti-patternsWhy this matters: Reference files contain battle-tested patterns and edge cases not covered in the skill overview. Explicit consultation during each skill execution ensures you use the latest patterns and prevents syntax errors.
{namespace="prod", app="api", level="error"} not just {namespace="prod"}Log Filtering:
{job="app"} |= "error" |= "timeout" # Contains both
{job="app"} |~ "error|fatal|critical" # Regex match
{job="app"} != "debug" # ExcludeJSON/logfmt Parsing:
{app="api"} | json | level="error" | status_code >= 500
{app="app"} | logfmt | caller="database.go"Pattern Extraction:
{job="nginx"} | pattern "<ip> - - [<_>] \"<method> <path>\" <status> <size>"Metrics:
# Rate
rate({job="app"} | json | level="error" [5m])
# Count by label
sum by (app) (count_over_time({namespace="prod"} | json [5m]))
# Error percentage
sum(rate({app="api"} | json | level="error" [5m])) / sum(rate({app="api"}[5m])) * 100
# Latency percentiles
quantile_over_time(0.95, {app="api"} | json | unwrap duration [5m])
# Top N
topk(10, sum by (error_type) (count_over_time({job="app"} | json | level="error" [1h])))Formatting:
{job="app"} | json | line_format "{{.level}}: {{.message}}"
{job="app"} | json | label_format env="{{.environment}}"IP Filtering (prefer label filter after parsing for precision):
{job="nginx"} | logfmt | remote_addr = ip("192.168.4.0/24")When to use this stage:
Present the query construction incrementally:
## Building Your Query Step-by-Step
### Step 1: Stream Selector (verify logs exist)
```logql
{app="api"}Test this first to confirm logs are flowing
{app="api"} |= "error"Reduces data before parsing
{app="api"} |= "error" | jsonNow you can filter on extracted labels
{app="api"} |= "error" | json | level="error"Final filter on parsed data
sum(count_over_time({app="api"} |= "error" | json | level="error" [5m]))Complete metric query
**Benefits of incremental building:**
1. Identify which step breaks (no results, parse errors)
2. Understand performance impact of each operation
3. Debug unexpected results by testing each stage
4. Learn LogQL query structure naturally
**Use AskUserQuestion** to offer incremental mode:
- Option: "Show step-by-step construction" vs "Show final query only"
### Stage 6: Provide Usage
1. **Final Query** with explanation
2. **How to Use**: Grafana panel, Loki alerting rules, `logcli query`, HTTP API
3. **Customization**: Labels to modify, thresholds to tune
## Advanced Techniques
### Multiple Parsers
```logql
{app="api"} | json | regexp "user_(?P<user_id>\\d+)"sum(sum_over_time({app="api"} | json | unwrap duration [5m])){service_name=`app`} |> "<_> level=debug <_>"{app="api"} | json | (status_code >= 400 and status_code < 500) or level="error"sum(rate({app="api"} | json | level="error" [5m])) - sum(rate({app="api"} | json | level="error" [5m] offset 1d)){app="api"} | json | keep namespace, pod, level
{app="api"} | json | drop pod, instanceNote: LogQL has no
dedupordistinctoperators. Use metric aggregations likesum by (field)for programmatic deduplication.
High-cardinality data without indexing (trace_id, user_id, request_id):
# Filter AFTER stream selector, NOT in it
{app="api"} | trace_id="abc123" | json | level="error"Place structured metadata filters BEFORE parsers:
# ACCELERATED
{cluster="prod"} | detected_level="error" | logfmt | json
# NOT ACCELERATED
{cluster="prod"} | logfmt | json | detected_level="error"approx_topk(10, sum by (endpoint) (rate({app="api"}[5m])))sum(count_over_time({app="api"} | json | level="error" [5m])) or vector(0)discover_log_levels: true (stored as structured metadata)| Function | Description |
|---|---|
rate(log-range) | Entries per second |
count_over_time(log-range) | Count entries |
bytes_rate(log-range) | Bytes per second |
absent_over_time(log-range) | Returns 1 if no logs |
| Function | Description |
|---|---|
sum_over_time, avg_over_time, max_over_time, min_over_time | Aggregate numeric values |
quantile_over_time(φ, range) | φ-quantile (0 ≤ φ ≤ 1) |
first_over_time, last_over_time | First/last value |
sum, avg, min, max, count, stddev, topk, bottomk, approx_topk, sort, sort_desc
With grouping: sum by (label1, label2) or sum without (label1)
| Function | Description |
|---|---|
duration_seconds(label) | Convert duration string |
bytes(label) | Convert byte string (KB, MB) |
label_replace(rate({job="api"} |= "err" [1m]), "foo", "$1", "service", "(.*):.*")| logfmt [--strict] [--keep-empty]--strict: Error on malformed entries--keep-empty: Keep standalone keys| json # All fields
| json method="request.method", status="response.status" # Specific fields
| json servers[0], headers="request.headers[\"User-Agent\"]" # Nested/arrayCommon functions for line_format and label_format:
String: trim, upper, lower, replace, trunc, substr, printf, contains, hasPrefix
Math: add, sub, mul, div, addf, subf, floor, ceil, round
Date: date, now, unixEpoch, toDate, duration_seconds
Regex: regexReplaceAll, count
Other: fromJson, default, int, float64, __line__, __timestamp__
See assets/common_queries.logql for detailed usage.
# Alert when error rate exceeds 5%
(sum(rate({app="api"} | json | level="error" [5m])) / sum(rate({app="api"}[5m]))) > 0.05
# With vector() to avoid "no data"
sum(rate({app="api"} | json | level="error" [5m])) or vector(0) > 10| Issue | Solution |
|---|---|
| No results | Check labels exist, verify time range, test stream selector alone |
| Query slow | Use specific selectors, filter before parsing, reduce time range |
| Parse errors | Verify log format matches parser, test JSON validity |
| High cardinality | Use line filters not label filters for unique values, aggregate |
Trigger context7 MCP or WebSearch when the query involves ANY of these:
| Trigger | Topic to Search | Tool to Use |
|---|---|---|
| User mentions "Loki 3.x" features | structured metadata, bloom filters, detected_level | context7 MCP |
approx_topk function needed | approx_topk probabilistic | context7 MCP |
Pattern match operators (|>, !>) | pattern match operator | context7 MCP |
vector() function for alerting | vector function alerting | context7 MCP |
| Recording rules configuration | recording rules loki | context7 MCP |
| Unclear syntax or edge cases | Specific function or operator | context7 MCP |
| Version-specific behavior questions | Version + feature | WebSearch |
| Grafana Alloy integration | grafana alloy loki | WebSearch |
context7 MCP (preferred - authoritative docs):
1. mcp__context7__resolve-library-id with libraryName="grafana loki"
2. mcp__context7__get-library-docs with context7CompatibleLibraryID and topic="[specific topic]"WebSearch (fallback for latest features):
WebSearch query: "Grafana Loki LogQL [topic] documentation [year]"When user asks for "error tracking with trace correlation in Loki 3.x":
mcp__context7__get-library-docs with topic="structured metadata trace_id"|>, !>)approx_topk functionDeprecations: Promtail (use Alloy), BoltDB store (use TSDB with v13 schema)
Install with Tessl CLI
npx tessl i pantheon-ai/logql-generator@0.1.0