Grafana Skill
When to use
Use this skill when:
- Creating or editing Grafana dashboards (JSON provisioning)
- Writing Loki LogQL queries for log analysis
- Configuring alerting rules or contact points
- Working on a Grafana module (if the project has one)
- Debugging log-based issues using Loki data
Procedure: Create Grafana dashboard
- Inspect existing Grafana assets — Check the project's Grafana location (Docker config,
dashboards/, provisioning) and existing datasources (Loki, Prometheus) before adding panels.
- Decide signals and layout — Pick the queries (LogQL / PromQL) and panel types per signal; reuse naming and label conventions below.
- Author the dashboard JSON — Add provisioned JSON under the dashboards path; wire datasources and template variables.
- Verify — Reload Grafana, confirm panels render against real data, and check alert rules fire on synthetic test inputs.
Project setup
Check if the project has a dedicated Grafana module or Docker config. Typical structure:
{grafana-location}/
├── .docker/
│ ├── grafana/
│ │ ├── dashboards/ ← Dashboard JSON files (provisioned)
│ │ ├── provisioning/
│ │ │ ├── datasources/ ← Loki, Prometheus data sources
│ │ │ └── dashboards/ ← Dashboard provisioning config
│ │ └── alerting/ ← Alert rules, contact points, notification policies
│ └── loki/
│ └── loki-config.yml ← Loki server configuration
├── App/
│ ├── Logging/ ← Loki timestamp processors
│ └── Console/Commands/ ← GrafanaPushDashboardsCommand
├── Docs/
│ └── Loki.md ← Loki documentation
└── Routes/console.php
Configuration
- Grafana config:
config/grafana.php
- Logging channels:
config/logging.php (Loki channels)
- Loki package:
itspire/monolog-loki via Monolog handler
Dashboard conventions
JSON provisioning
Dashboards are stored as JSON in .docker/grafana/dashboards/ and auto-provisioned.
Use GrafanaPushDashboardsCommand to push dashboards to remote Grafana instances.
Panel rules
- Always hide Loki metadata columns in table panels:
labelTypes, traceID, traceID (field) → add to excludeByName in the organize transformation.
- Verify column names match data semantics: If the timestamp represents
imported_at,
name the column "Imported", not "Uploaded".
- Use consistent time ranges: Default to "Last 24 hours" for operational dashboards.
- Add variables for customer FQDN, environment, and time range filters.
Naming
| Element |
Convention |
Example |
| Dashboard title |
Descriptive, Title Case |
"Import Overview (Loki)" |
| Panel title |
Short, descriptive |
"Import Results by Status" |
| Variable names |
lowercase, underscore |
$fqdn, $environment |
Loki LogQL
Query patterns
# Filter by service and status
{service="import_result"} |= "DONE" | json | status = "DONE"
# Count by label
sum by (status) (count_over_time({service="import_result"} | json [$__interval]))
# Filter by customer FQDN
{service="import_result", fqdn=~"$fqdn"} | json
Service labels
| Label |
Purpose |
Notes |
import_result |
Final import states (DONE, FAILED) |
Use for result dashboards |
import_snapshot |
Cron-based status snapshots |
Use for timeline dashboards |
service=import |
Legacy static label |
Do NOT query — use specific labels above |
Best practices
- Use
json parser for structured log entries
- Use
line_format for human-readable output in explore view
- Use
$__interval for rate/count queries (auto-adjusts to time range)
- Filter early in the pipeline (labels before line filters before parsers)
Alerting
Structure
.docker/grafana/alerting/
├── alert-rules.yml ← Alert conditions and thresholds
├── contact-points.yml ← Notification targets (Slack, email)
└── notification-policies.yml ← Routing rules (which alerts → which contacts)
Conventions
- Alert names: descriptive, include severity: "Import Failure Rate > 10% (Critical)"
- Use
for duration to avoid flapping (e.g., for: 5m)
- Group related alerts by folder/namespace
Integration with logging
The project uses structured logging via Monolog → Loki:
LokiTimestampProcessor — Adds precise timestamps to log entries
LokiTimestampChannelTap — Configures Loki channels with timestamp processing
- Import events are logged via
ImportEventLogger service
When adding new log entries for Grafana visualization:
- Use a dedicated log channel (defined in
config/logging.php)
- Log as JSON with consistent field names
- Add appropriate Loki labels for filtering
- Update or create a dashboard panel for the new data
Related
- Skill:
logging-monitoring — full monitoring stack overview
- Skill:
dashboard-design — visualization selection, layout, KPI strategies
- Skill:
traefik — HTTPS for Grafana embedding in the app
- Check the project for Grafana module or Docker config location
- Config:
config/grafana.php, config/logging.php (if applicable)
Output format
- Grafana dashboard JSON or LogQL/PromQL queries
- Panel configuration with data source and thresholds
- Alert rule definitions where applicable
Gotcha
- Loki queries use LogQL, not PromQL — the syntax is different despite looking similar.
- Don't create alerts without a clear notification channel — silent alerts are useless.
- Dashboard panels that query too much data (>7 days at full resolution) will timeout — use downsampling.
Do NOT
- Do NOT create panels without proper units and labels.
- Do NOT use alerting rules without a notification channel.
- Do NOT hardcode datasource names — use variables.
Auto-trigger keywords
- Grafana
- Loki
- dashboard
- log query
- alerting
- monitoring panel
1---2name: grafana3description: Use when working with Grafana — dashboards, Loki LogQL queries, alerting rules, monitoring panels — even when the user just says 'build me a dashboard' or 'query the logs' without naming Grafana.4---56# Grafana Skill78## When to use910Use this skill when:11- Creating or editing Grafana dashboards (JSON provisioning)12- Writing Loki LogQL queries for log analysis13- Configuring alerting rules or contact points14- Working on a Grafana module (if the project has one)15- Debugging log-based issues using Loki data1617## Procedure: Create Grafana dashboard18191. **Inspect existing Grafana assets** — Check the project's Grafana location (Docker config, `dashboards/`, provisioning) and existing datasources (Loki, Prometheus) before adding panels.202. **Decide signals and layout** — Pick the queries (LogQL / PromQL) and panel types per signal; reuse naming and label conventions below.213. **Author the dashboard JSON** — Add provisioned JSON under the dashboards path; wire datasources and template variables.224. **Verify** — Reload Grafana, confirm panels render against real data, and check alert rules fire on synthetic test inputs.2324### Project setup2526Check if the project has a dedicated Grafana module or Docker config. Typical structure:2728```29{grafana-location}/30├── .docker/31│ ├── grafana/32│ │ ├── dashboards/ ← Dashboard JSON files (provisioned)33│ │ ├── provisioning/34│ │ │ ├── datasources/ ← Loki, Prometheus data sources35│ │ │ └── dashboards/ ← Dashboard provisioning config36│ │ └── alerting/ ← Alert rules, contact points, notification policies37│ └── loki/38│ └── loki-config.yml ← Loki server configuration39├── App/40│ ├── Logging/ ← Loki timestamp processors41│ └── Console/Commands/ ← GrafanaPushDashboardsCommand42├── Docs/43│ └── Loki.md ← Loki documentation44└── Routes/console.php45```4647### Configuration4849- **Grafana config:** `config/grafana.php`50- **Logging channels:** `config/logging.php` (Loki channels)51- **Loki package:** `itspire/monolog-loki` via Monolog handler5253## Dashboard conventions5455### JSON provisioning5657Dashboards are stored as JSON in `.docker/grafana/dashboards/` and auto-provisioned.58Use `GrafanaPushDashboardsCommand` to push dashboards to remote Grafana instances.5960### Panel rules6162- **Always hide Loki metadata columns** in table panels:63 `labelTypes`, `traceID`, `traceID (field)` → add to `excludeByName` in the `organize` transformation.64- **Verify column names match data semantics:** If the timestamp represents `imported_at`,65 name the column "Imported", not "Uploaded".66- **Use consistent time ranges:** Default to "Last 24 hours" for operational dashboards.67- **Add variables** for customer FQDN, environment, and time range filters.6869### Naming7071| Element | Convention | Example |72|---|---|---|73| Dashboard title | Descriptive, Title Case | "Import Overview (Loki)" |74| Panel title | Short, descriptive | "Import Results by Status" |75| Variable names | lowercase, underscore | `$fqdn`, `$environment` |7677## Loki LogQL7879### Query patterns8081```logql82# Filter by service and status83{service="import_result"} |= "DONE" | json | status = "DONE"8485# Count by label86sum by (status) (count_over_time({service="import_result"} | json [$__interval]))8788# Filter by customer FQDN89{service="import_result", fqdn=~"$fqdn"} | json90```9192### Service labels9394| Label | Purpose | Notes |95|---|---|---|96| `import_result` | Final import states (DONE, FAILED) | Use for result dashboards |97| `import_snapshot` | Cron-based status snapshots | Use for timeline dashboards |98| `service=import` | Legacy static label | Do NOT query — use specific labels above |99100### Best practices101102- Use `json` parser for structured log entries103- Use `line_format` for human-readable output in explore view104- Use `$__interval` for rate/count queries (auto-adjusts to time range)105- Filter early in the pipeline (labels before line filters before parsers)106107## Alerting108109### Structure110111```112.docker/grafana/alerting/113├── alert-rules.yml ← Alert conditions and thresholds114├── contact-points.yml ← Notification targets (Slack, email)115└── notification-policies.yml ← Routing rules (which alerts → which contacts)116```117118### Conventions119120- Alert names: descriptive, include severity: "Import Failure Rate > 10% (Critical)"121- Use `for` duration to avoid flapping (e.g., `for: 5m`)122- Group related alerts by folder/namespace123124## Integration with logging125126The project uses structured logging via Monolog → Loki:127128- **`LokiTimestampProcessor`** — Adds precise timestamps to log entries129- **`LokiTimestampChannelTap`** — Configures Loki channels with timestamp processing130- **Import events** are logged via `ImportEventLogger` service131132When adding new log entries for Grafana visualization:1331. Use a dedicated log channel (defined in `config/logging.php`)1342. Log as JSON with consistent field names1353. Add appropriate Loki labels for filtering1364. Update or create a dashboard panel for the new data137138## Related139140- **Skill:** `logging-monitoring` — full monitoring stack overview141- **Skill:** `dashboard-design` — visualization selection, layout, KPI strategies142- **Skill:** `traefik` — HTTPS for Grafana embedding in the app143- Check the project for Grafana module or Docker config location144- **Config:** `config/grafana.php`, `config/logging.php` (if applicable)145146147## Output format1481491. Grafana dashboard JSON or LogQL/PromQL queries1502. Panel configuration with data source and thresholds1513. Alert rule definitions where applicable152153## Gotcha154155- Loki queries use LogQL, not PromQL — the syntax is different despite looking similar.156- Don't create alerts without a clear notification channel — silent alerts are useless.157- Dashboard panels that query too much data (>7 days at full resolution) will timeout — use downsampling.158159## Do NOT160161- Do NOT create panels without proper units and labels.162- Do NOT use alerting rules without a notification channel.163- Do NOT hardcode datasource names — use variables.164165## Auto-trigger keywords166167- Grafana168- Loki169- dashboard170- log query171- alerting172- monitoring panel