cx-dashboards
Build and deploy a Coralogix dashboard for a given service from its logs, spans, metrics, and service specs. Discovers telemetry via cx CLI commands, emits importable Coralogix JSON, verifies every PromQL and DataPrime query live through the `cx` CLI, and creates or updates dashboards via `cx dashboards create` and `cx dashboards replace`. Use whenever the user asks to create, build, generate, deploy, update, replace, or modify a Coralogix dashboard, monitoring dashboard, or observability dashboard for a service, app, or pipeline.
- 3,206
- installs
- 0 up / 0 down, score 0.00
- votes
- 124
- repo stars
- Updated
- less than a month ago
- In the catalog since
- 1 Oct 2026
- From
- coralogix/cx-cli
Skill instructions
Rendered from the published SKILL.md, 347 lines.
Create Coralogix Dashboard
Produces a Coralogix dashboard for a target service and deploys it via the cx CLI. Workflow: discover the service's telemetry, align on intent with the user, draft a plan, emit the JSON, live-verify every query through cx, then create the dashboard in a chosen folder.
Only use metric names, log fields, and span attributes you can cite from the service's code, README, configuration, or a live query that returned a result. Do not invent them.
Reference files
Load these files for domain-specific guidance:
| Task | Reference |
|---|---|
| DataPrime query syntax | references/dataprime-reference.md |
| PromQL query syntax, counters vs gauges, histograms | references/promql-guidelines.md |
| Log field discovery, query patterns, wildfind policy | references/logs-querying.md |
| Span field discovery, latency analysis, trace queries | references/spans-querying.md |
Dashboard-specific query gotchas (${__range}, promqlQueryType) |
references/query-syntax.md |
| Widget JSON templates | references/widget-templates.md |
For choosing the right signal (metrics / logs / traces), use cx-telemetry-querying.
Dashboard Management
Beyond creating dashboards, use these commands to manage existing ones:
| Command | Purpose |
|---|---|
cx dashboards catalog -o json |
List all dashboards in the catalog |
cx dashboards get <id> -o json |
Get a dashboard definition (useful as a template) |
cx dashboards folders list -o json |
List dashboard folders |
cx dashboards folders create --name "Name" |
Create a dashboard folder |
cx dashboards folders create --name "Sub" --parent-id <id> |
Create a nested folder |
cx dashboards replace --from-file dashboard.json |
Replace an existing dashboard with updated JSON |
cx dashboards check --from-file dashboard.json |
Validate a dashboard definition without persisting (server-side strict check; exits non-zero on errors) |
cx dashboards check <dashboard-id> |
Validate a stored dashboard by id |
To update an existing dashboard:
cx dashboards get <dashboard-id> -o json > dashboard.json
# Edit dashboard.json (change name, modify widgets, etc.)
cx dashboards replace --from-file dashboard.json
To duplicate a dashboard as a new copy:
cx dashboards get <dashboard-id> -o json > dashboard.json
# Remove the "id" field, then create as new:
cx dashboards create --from-file dashboard.json
Workflow
Track progress through this checklist:
Dashboard Progress:
- [ ] Phase 1: Discover telemetry & business meaning
- [ ] Phase 2: Gather dashboard specifications from user
- [ ] Phase 3: Draft internal dashboard plan (sections/rows/widgets)
- [ ] Phase 4: Generate the Coralogix JSON
- [ ] Phase 5: Live-verify every query through the cx CLI
- [ ] Phase 6: Self-verify structure against the checklist
- [ ] Phase 7: Server-side validation via `cx dashboards check`
- [ ] Phase 8: Deploy via `cx dashboards create`
- [ ] Phase 9: Share the dashboard link with the user
Proceed in order. Don't jump to Phase 4 before the user approves the Phase 3 plan, and don't run Phase 8 before Phases 5–7 all pass. Phase 9 is mandatory — the workflow is not done until the user has a clickable link.
Phase 1: Discover telemetry & business meaning
For the target service, gather:
- Business purpose - read
README.mdand the top-level entrypoint (main.*,index.*,cmd/main.go, etc.). Summarize in 2–3 sentences what it does, its key stages, and what can go wrong. - Metrics - for each candidate keyword (service name, subsystem, verbs like
request,error,latency,dlq) runcx metrics search --name '*<keyword>*'. When a metric looks promising, list its labels withcx metrics get-labels <metric>. Only use namescx metrics searchreturns - this is what prevents invented metrics from reaching Phase 5. Cross-check the service's instrumentation (prometheus_client,promauto.NewCounter/Histogram/Gauge, OTel meters,prom-client, Micrometer,metrics.py) for semantics and histogram buckets (_sum,_count,_bucket). - Logs - discover custom
$d.*fields withcx search-fields "<description>" --dataset logsbefore assuming a field exists. Sample message templates and severity withcx logs "filter \$l.applicationname == '<app>'" --limit 5 -o json. Standard fields ($m.severity,$m.timestamp,$l.applicationname,$l.subsystemname) don't need discovery. - Spans / traces - discover span attributes with
cx search-fields "<description>" --dataset spans. Sample withcx spans "filter \$l.serviceName == '<svc>'" --limit 5 -o json. Error conventions vary ($d.tags.error,$d.http.status_code); check samples before filtering. - Message buses & DLQs - grep for Kafka, RabbitMQ, SQS, Pub/Sub clients and any
dlq/DLQreferences. Note topic/queue names for DLQ panels. - Service configuration - check
meta.yaml, Helmvalues.yaml,Deployment,Dockerfile,chart.yaml. Extract:- The
applicationname/subsystemnamelabel values as they appear in Coralogix. - Tenant/account/team identifiers used as metric or log labels.
- Deployment environments (
prod,staging,dev, …).
- The
If the signal for a question is ambiguous (e.g. "how much revenue last week"), delegate to cx-telemetry-querying first.
Produce a short internal summary before moving on. If critical telemetry is missing (e.g. no metrics), surface that to the user and ask whether they want a log-only or trace-only dashboard.
Phase 2: Gather dashboard specifications
Ask the user a focused set (≤6). Prefer AskQuestion:
- Audience & use - on-call triage, product/business tracking, capacity planning, customer success?
- Default time range - typical viewing window (e.g. 24h, 7d). Queries still use
${__range}so users can zoom. - Slicing dimensions - top-level filters (
tenant_id,account_id,subsystem_name,region,env, …). - Environment scope - which environments to include/exclude (common default: exclude
dev,staging,test). - SLO-ish signals - success-rate, latency, or throughput targets to highlight?
- Priorities - what to see first (drives row ordering and which section is
collapsed: true).
Don't block on answers you can reasonably infer - state the inference and continue.
Phase 3: Draft the internal plan
Write a markdown plan the user can approve before JSON generation:
## Dashboard: <Service> - <Purpose>
Show the whole skill (227 more lines)
Section 1: (collapsed: false)
- Row 1: [widget type] - <what it shows> - source: metrics|logs|spans</li> <li>Row 2: ...</li> </ul> <h3>Section 2: <Deep dive> (collapsed: false)</h3> <p>...</p> <h3>Section N: <Logs & errors> (collapsed: true)</h3> <p>...</p> <h3>Top-level filters</h3> <ul> <li><label> (<source>)</li> </ul> <h3>Assumptions / gaps</h3> <ul> <li>...</li> </ul> <pre><code> **Section design**: - First section: at-a-glance health (gauges + key rates), always expanded. - Pair related time-series in the same row (rate + latency). - Final section (raw logs, rare breakdowns): `collapsed: true`. - Aim for 3–5 sections, 6–20 widgets total. **Widget-type selection**: | Signal | Widget type | |---|---| | Single headline number from a metrics query (count, % success, totals) | `gauge` (Coralogix calls this "stat") | | Breakdown across ≤8 categories from a metrics query | `pieChart` | | Change over time from a metrics query (rate, latency, count per bucket) | `lineChart` | | Top-N tables and listings, from any query (PromQL `topk` included) | `dynamic` with a `table` visualization (never legacy `dataTable`) | | Query over logs, spans or DataPrime | `dynamic`, visualization per the shape table | Don't use other widget types unless the user asks. **Logs, spans and DataPrime widgets are `dynamic`, and visualized.** Never emit the legacy `dataTable` widget, for any query: a PromQL top-N table is a `dynamic` widget with a `metrics` query and a `table` visualization. Use `definition.dynamic` and set its `visualization` to the chart the query's shape calls for; `table` is right only for raw rows the user wants to read. Alias every column in the query and map the visualization to the aliases. Shape-to-visualization table and templates: `references/widget-templates.md` → *Widget: dynamic*. Wait for the user to approve or adjust the plan before emitting JSON. --- ## Phase 4: Generate the Coralogix JSON Produce a single JSON document following [`references/widget-templates.md`](references/widget-templates.md). Key rules: 1. **Top-level shape**: </code></pre> <p> { "id": "<21-char-nanoid>", "name": "<Dashboard Name>", "layout": { "sections": [ ... ] }, "variables": [], "variablesV2": [], "filters": [ ... ], "relativeTimeFrame": "<seconds>s", "annotations": [], "off": {}, "actions": [] }</p> <pre><code>2. **IDs** - fresh UUIDs for every `section`, `row`, `widget`, and query `id`. 3. **Row height** - `"appearance": { "height": 19 }` unless there's a reason to change. 4. **Section options** - include `options.custom.name`, `collapsed`, and `color.predefined: "SECTION_PREDEFINED_COLOR_UNSPECIFIED"`. 5. **Filters** - one entry per slicing dimension from Phase 2. Default operator `equals` with empty `values` so users can fill in. Use `notEquals` for environment exclusions (see [`references/widget-templates.md`](references/widget-templates.md)). 6. **relativeTimeFrame** - default `"172800s"` (48h) unless the user specified otherwise. For query syntax follow [`references/query-syntax.md`](references/query-syntax.md); for the full query languages load [`references/dataprime-reference.md`](references/dataprime-reference.md) and [`references/promql-guidelines.md`](references/promql-guidelines.md). --- ## Phase 5: Live-verify every query through the cx CLI Every PromQL and DataPrime query in the draft has to successfully run through `cx` before Phase 8. This catches invented metric names, typoed field paths, and malformed pipelines. ### Frequent vs Archive (what / when / where in JSON) **What**: - **Frequent** (`TIER_FREQUENT_SEARCH`): hot tier for fast search on recent logs/spans. - **Archive** (`TIER_ARCHIVE`): cold tier for older logs/spans (long-term). **When to choose**: - Choose **Frequent** for on-call and recent investigations (hours/days). - Choose **Archive** for long lookbacks (weeks/months) or when the time range is beyond hot retention. The two languages are verified against different windows: - **PromQL**: map `relativeTimeFrame` to a `$RANGE` token (e.g. `48h` for `172800s`), substitute `${__range}` with `[$RANGE]` for the CLI call, then restore `${__range}` in the JSON before Phase 6. Range vectors are window-sensitive, so the check has to match what the dashboard will evaluate. - **DataPrime**: verify against a fixed short window (`now-15m` → `now`, `--limit 1`). The goal is syntax / field / pipeline validation, not data-presence on the dashboard's window — a short window is faster and a cleaner fail signal. Full procedure (CLI invocations, `$RANGE` mapping table, retry budget, failure modes): [`references/verification.md`](references/verification.md). If a query can't be made to pass within the retry budget, surface it to the user with the CLI error verbatim - don't ship a broken widget. --- ## Phase 6: Self-verify structure Run this checklist against the final JSON. Fix and re-check if any item fails before Phase 7. ### Query syntax (dashboard-specific) - [ ] Every PromQL range vector in a metrics widget uses `[${__range}]` - never `[$__range]`, never `[5m]` (unless the panel is intentionally a sliding window). - [ ] `promqlQueryType` is `PROM_QL_QUERY_TYPE_INSTANT` for single-value widgets (gauge, pieChart, dynamic table). Omitted for `lineChart`. - [ ] DataPrime log queries use `$d.<field>` / `$l.applicationname` / unquoted severity enums (full rules: [`references/dataprime-reference.md`](references/dataprime-reference.md)). - [ ] Every DataPrime widget query starts with `source logs` or `source spans` (dashboard widgets require the source prefix; Phase 5 verification strips it before handing the pipeline to `cx logs` / `cx spans`). - [ ] Success-rate denominators wrapped in `clamp_min(..., 1)`. - [ ] Histogram queries use the correct suffix (`_sum`, `_count`, `_bucket`). - [ ] Widget queries are valid **without** the dashboard-level `filters` - Coralogix injects them at render time. ### Structure - [ ] Each section has `id.value`, `rows`, and `options.custom`. - [ ] Each row has `id.value`, `appearance.height`, and `widgets`. - [ ] Each widget has a unique `id.value` and a `definition` with exactly one of `gauge` / `pieChart` / `lineChart` / `dynamic`. - [ ] Every gauge has numeric `min` and `max`, and `min < max`. - [ ] Success-rate gauges use `thresholdType: "THRESHOLD_TYPE_ABSOLUTE"` with green at high values; error/DLQ gauges use red at high values. - [ ] Metrics "Total" / "stat" widgets are encoded as `gauge`. A logs, spans or DataPrime total is a `dynamic` widget with a `stat` visualization. - [ ] Top-level `filters` includes each slicing dimension from Phase 2. - [ ] All IDs are freshly generated UUIDs, unique within the document. - [ ] Every `queryDisplaySettings[].queryId` equals the `id` of a `queryDefinitions[]` entry in the same widget. `cx dashboards check` accepts a mismatch, and the widget then renders empty. ### Content - [ ] Dashboard name is descriptive (`"<Service> - <Purpose>"`). - [ ] Widget titles are short, human-readable, and match what the query computes. - [ ] The logs/errors section is `collapsed: true` unless the user said otherwise. --- ## Phase 7: Server-side validation via `cx dashboards check` Phase 6 caught structural issues by hand. This phase runs the whole dashboard through the Coralogix Dashboard Service's strict validator (`CheckDashboard`) — the same validation `create`/`replace` apply on write, plus a superset that compiles every PromQL/DataPrime query and enforces required ids for variables and filters. 1. Run `cx dashboards check --from-file /tmp/cx-dashboard-<slug>.json`. 2. If the command exits non-zero, the output lists issues with `severity`, `location` (an RFC 6901 JSON Pointer into the dashboard, e.g. `/sections/0/rows/1/widgets/2`), and `message`. Fix each issue in the JSON (loop back to Phase 4), re-run Phase 5 for any query that changed, then re-run this phase. 3. When `check` exits 0 (text output: `Dashboard is valid (no issues)`), proceed to Phase 8. `check` is read-only — it never persists the dashboard. Warnings (`SEVERITY_WARNING`) print but do not fail the gate; only errors (`SEVERITY_ERROR`) cause a non-zero exit. In multi-profile fan-out, any profile returning errors fails the command. --- ## Phase 8: Deploy via `cx dashboards create` Don't tell the user to paste JSON into the Coralogix UI - deploy it directly. 1. List folders: `cx dashboards folders list -o json`. 2. Suggest the best folder match (team, product area, or a folder named after the service). Default to root (omit `--folder`) if nothing fits. 3. Write the verified JSON to a temp file and run `cx dashboards create --from-file /tmp/cx-dashboard-<slug>.json --folder <id>`. The CLI generates the `requestId` envelope and prints the created dashboard ID. Full procedure (folder-picking UX, command templates, idempotency note): [`references/deploy.md`](references/deploy.md). On failure: show the CLI error verbatim and return to Phase 5. The most common cause is a query that parses locally but the live API rejects. --- ## Phase 9: Share the dashboard link The workflow is **not done** until the user has a clickable link to the dashboard. Printing the ID alone forces the user to navigate the Coralogix UI by hand, which defeats the point of automating deployment. After Phase 8 succeeds, capture the `View in Coralogix: <url>` line that `cx dashboards create` prints to stderr (see [`references/deploy.md`](references/deploy.md) § "Share the link" for when it's omitted) and emit the output template below. Render the dashboard **name** as the link text — that's what the user clicks. Linking to a dashboard found via `cx dashboards catalog` (rather than one you just created) works differently: `catalog` prints only one link, to the catalog page, not a per-dashboard link. To link to one specific dashboard from that list, build `<base>/dashboards/<dashboard_id>`, where `<base>` is the console URL already seen in a `View in Coralogix: <base>/...` line printed by any `cx dashboards` command this session — never fabricate `<base>` yourself, and never invent it if no such line has been printed yet. --- ## Output format for the user When `cx dashboards create` printed a `View in Coralogix:` link: </code></pre> <h2>Plan</h2> <p><the approved Phase 3 plan></p> <h2>Verification</h2> <ul> <li>PromQL queries verified: <N>/<N></li> <li>DataPrime queries verified: <N>/<N></li> </ul> <h2>Deployed</h2> <ul> <li>Dashboard: <strong><a href="url%20from%20the%20View%20in%20Coralogix%20line"><Name></a></strong></li> <li>ID: <code><id></code></li> <li>Folder: <code><folder name or "root"></code></li> <li>Profile: <code><cx profile></code></li> </ul> <p>Open it: <a href="url%20from%20the%20View%20in%20Coralogix%20line"><Name></a></p> <p>Adjust filter values (e.g. <code>account_id</code>) after opening it.</p> <pre><code> When `cx dashboards create` did **not** print a `View in Coralogix:` line (no console link could be resolved for the profile and no `console_url` override configured), omit the link entirely — do not invent a URL. Use this template instead: </code></pre> <h2>Plan</h2> <p><the approved Phase 3 plan></p> <h2>Verification</h2> <ul> <li>PromQL queries verified: <N>/<N></li> <li>DataPrime queries verified: <N>/<N></li> </ul> <h2>Deployed</h2> <ul> <li>Dashboard: <strong><Name></strong> (open via the Coralogix UI; ID <code><id></code>)</li> <li>ID: <code><id></code></li> <li>Folder: <code><folder name or "root"></code></li> <li>Profile: <code><cx profile></code></li> </ul> <p>Adjust filter values (e.g. <code>account_id</code>) after opening it.</p> <pre><code> --- ## References - Dashboard query gotchas & cross-references: [`references/query-syntax.md`](references/query-syntax.md) - Widget JSON templates: [`references/widget-templates.md`](references/widget-templates.md) - Live-verification procedure: [`references/verification.md`](references/verification.md) - Deploy procedure: [`references/deploy.md`](references/deploy.md) - DataPrime language reference: [`references/dataprime-reference.md`](references/dataprime-reference.md) - PromQL reference: [`references/promql-guidelines.md`](references/promql-guidelines.md) - Log querying patterns: [`references/logs-querying.md`](references/logs-querying.md) - Span querying patterns: [`references/spans-querying.md`](references/spans-querying.md) - Inline DataPrime help: `cx dataprime list`, `cx dataprime show <command>` - Coralogix Custom Dashboards docs: <https://www.coralogix.com/docs/user-guides/custom-dashboards/introduction/> ### Related Skills - **`cx-observability-setup`** - full monitoring setup workflow (views, webhooks, notifications, integrations) - **`cx-slos`** - SLO-connected reliability targets to surface on dashboards - **`cx-cases`** - triage the cases raised against the services a dashboard monitors - **`cx-telemetry-querying`** - discover the right telemetry signal before building dashboards </code></pre>