From grafana-app-sdk
References Grafana Loki LogQL syntax for log/metric queries, parsers (json/logfmt/pattern/regexp/unpack), label filters, architecture, and ingestion via Promtail/Alloy/Fluent Bit. For writing queries, configuring pipelines, troubleshooting logs.
npx claudepluginhub grafana/skills --plugin grafana-app-sdkThis skill uses the workspace's default tool permissions.
> **Docs**: https://grafana.com/docs/loki/latest/
Generates LogQL queries, stream selectors, metric queries, and alerting rules for Grafana Loki via interactive workflow handling versions, labels, and use cases like debugging or dashboards.
Configures centralized log aggregation with Loki/Promtail or ELK stack, including parsing, label extraction, retention policies, and metrics correlation for multi-service troubleshooting.
Query logs, list and manage sources, perform structured searches with SQL-like queries, set up log-based alerts, and analyze logs in Better Stack (Logtail).
Share bugs, ideas, or general feedback.
Indexes only metadata (labels), not full log content — dramatically cheaper than full-text search systems.
{app="nginx"} # exact match
{app!="nginx"} # not equal
{app=~"nginx|apache"} # regex match
{app!~"debug.*"} # regex not match
{app="nginx", env="prod"} # AND (multiple labels)
{app="nginx"} |= "error" # contains string
{app="nginx"} != "info" # does not contain
{app="nginx"} |~ "error|warn" # regex match
{app="nginx"} !~ "health.*check" # regex not match
{app="nginx"} |= `"status":5` # backtick avoids escaping
# JSON
{app="api"} | json
{app="api"} | json status="http_status", path="request.path"
# Logfmt
{app="api"} | logfmt
{app="api"} | logfmt --strict
{app="api"} | logfmt --keep-empty
# Pattern (positional, _ discards)
{app="nginx"} | pattern `<ip> - - <_> "<method> <uri> <_>" <status> <bytes>`
# Regexp (named capture groups)
{app="nginx"} | regexp `(?P<method>\w+) (?P<path>\S+) HTTP/(?P<version>\S+)`
# Unpack (unwrap Promtail packed labels)
{app="api"} | unpack
{app="api"} | json | status >= 500
{app="api"} | json | status == 200 and method != "OPTIONS"
{app="api"} | logfmt | duration > 1s
{app="api"} | json | level =~ "error|warn"
{app="api"} | json | bytes > 20MB
{app="api"} | json | path != "/healthz"
{app="api"} | json | line_format "{{.method}} {{.path}} -> {{.status}} ({{.duration}})"
{app="api"} | logfmt | line_format `{{.level | upper}}: {{.msg}}`
{app="api"} | logfmt | label_format new_name=old_name
{app="api"} | logfmt | label_format severity=level, svc=app
{app="api"} | logfmt | label_format msg=`{{.level}}: {{.message}}`
{app="api"} | json | drop filename, level="debug"
{app="api"} | json | keep level, status, method
{app="cli-tool"} | decolorize
# Requests per second
rate({app="nginx"}[5m])
# Total log lines in window
count_over_time({app="nginx"}[1h])
# Bytes per second
bytes_rate({app="nginx"}[5m])
# Total bytes
bytes_over_time({app="nginx"}[1h])
# Returns 1 if no logs in range (for absence alerting)
absent_over_time({app="nginx"}[5m])
# Error rate by service
sum(rate({env="prod"} |= "error" [5m])) by (app)
# Top 5 most active services
topk(5, sum(rate({env="prod"}[5m])) by (app))
# Total errors across all services
sum(count_over_time({env="prod"} |= "error" [5m]))
# Average request duration from logfmt
avg_over_time({app="api"} | logfmt | unwrap duration [5m])
# 95th percentile latency
quantile_over_time(0.95, {app="api"} | logfmt | unwrap duration [5m]) by (app)
# Sum of bytes from JSON logs
sum_over_time({app="api"} | json | unwrap bytes [5m])
# With conversion (duration string → seconds)
avg_over_time({app="api"} | logfmt | unwrap duration | duration_seconds [5m])
# Compare current rate vs 1 hour ago
rate({app="nginx"}[5m]) / rate({app="nginx"}[5m] offset 1h)
sum(rate({env="prod"} |= "error" [5m])) by (service)
/
sum(rate({env="prod"}[5m])) by (service)
> 0.05
{app="api"} | logfmt | duration > 1s | line_format "SLOW: {{.method}} {{.path}} {{.duration}}"
{app="nginx"} | pattern `<ip> - - <_> "<method> <uri> <_>" <status> <bytes>` | status >= 500
{namespace="prod"} |~ `https?://\w+:\w+@`
loki.source.file "app" {
targets = [{__path__ = "/var/log/app/*.log", job = "app"}]
forward_to = [loki.process.parse.receiver]
}
loki.process "parse" {
forward_to = [loki.write.cloud.receiver]
stage.json {
expressions = { level = "level", msg = "message" }
}
stage.labels {
values = { level = "" }
}
stage.drop {
expression = ".*healthcheck.*"
}
}
loki.write "cloud" {
endpoint {
url = "https://logs-xxx.grafana.net/loki/api/v1/push"
basic_auth {
username = sys.env("LOKI_USER")
password = sys.env("GRAFANA_API_KEY")
}
}
external_labels = { cluster = "prod" }
}
discovery.kubernetes "pods" {
role = "pod"
}
loki.source.kubernetes "pods" {
targets = discovery.kubernetes.pods.targets
forward_to = [loki.write.cloud.receiver]
}
curl -X POST https://logs-xxx.grafana.net/loki/api/v1/push \
-u "user:apikey" \
-H 'Content-Type: application/json' \
-d '{
"streams": [{
"stream": { "app": "myapp", "env": "prod" },
"values": [
["1609459200000000000", "log line here"]
]
}]
}'
Push path: Client → Distributor → Ingester (WAL) → Object Storage (chunks)
Read path: Query → Query Frontend → Querier → Ingester + Store (chunks)
Components: