Skip to content
Loglinepre-release
Menu
docs/DASHBOARDS.mdEdit on GitHub

Dashboards

A dashboard is a grid of panels, each drawing one search: pie, bar, line and area charts, tables, gauges and single values. Base searches are defined once at the top of the page and extended by the panels that reference them, and inputs (text boxes and dropdowns) feed values into the searches as $tokens$. Dashboards are saved on the server, so everyone who signs in sees the same ones; a user may create dashboards and change or delete their own, an admin any, and a readonly account views them.

Using them#

Dashboards in the top bar lists them, with a filter box; New dashboard opens the editor on an empty one and Import JSON creates one from an exported file. On the search page, Add to dashboard next to a result puts that search on a dashboard of yours, or on a new one, as the visualization that fits the result. In the editor:

  • The title and description are at the top, with the default time range (the range every panel uses unless it has its own) and the auto-refresh period.
  • Inputs is the bar under the head. Add input gives one an id (lower-case letters, digits and underscores), a label, a type and a default. A text box passes what is typed; a dropdown lists fixed choices (one per line, value or Label=value) or the rows of a search of its own, such as index=app | stats count by service, with the value and label columns named (the first column when not) and an optional All choice. The searches read an input as $id$.
  • Base searches is the strip under the inputs. Add base search gives it an id and a query; a base search can have its own time range. Removing one that panels extend turns its query into theirs.
  • Add panel opens the panel editor: a title, the visualization, the search (its own, or a base search extended with commands that start with a pipe, such as | stats count by status), a time range, and the settings of the chosen visualization, with a live preview on the right (Enter in the query runs it). Add to dashboard puts it on the grid.
  • On the grid, drag a panel by its head and resize it by its edges. Each panel's tools open it in the search page, download its result as CSV, edit, duplicate or remove it.
  • Save stores the dashboard; Cancel drops the changes. Saving a dashboard someone else changed in the meantime is refused, so nobody's work is overwritten unseen. Export JSON downloads the dashboard as the document below, for a backup, a copy on another host, or editing by hand.

Viewing a dashboard, the inputs sit under the head; changing one reruns the panels whose searches mention it. The time picker overrides the dashboard's default range for that visit; a base search's or a panel's own range still wins, as it does in the editor. The URL carries the range and the input values (#dashboards/web?earliest=-7d&in.host=web1), so a link shares a filled-in view. Refresh reruns every panel (the time it last ran is shown beside it), and the auto-refresh select keeps doing so. The base-search strip shows, per base search, how many panels extend it, the range they ran over, the slowest one and the first error. Clicking a slice or a bar opens the search page with that value added to the search, or, when the panel says so, sets a dashboard input to the value (a pie of services that fills the service input, say); the arrow in a panel's head opens its whole search. Present shows the dashboard alone, full screen, for a wall display; ?kiosk=1 on the URL opens it that way. The menu button beside it exports the dashboard as JSON or duplicates it.

The Light/Dark switch redraws every chart in the other palette.

Inputs and tokens#

An input's value replaces $id$ wherever a base search, a panel's commands or a panel's own search mentions it, before the search runs: with the base search index=app service=$svc$ and checkout chosen, the panels run index=app service=checkout .... The value is put in as it is, so a token can stand for a field value, a field name (| stats count by $field$) or a whole clause; an input left empty, or a dropdown's All choice, becomes *, so a blank text box matches everything. The server checks on save that every token names an input and that the searches parse with the defaults in place. A dropdown fed by a search runs that search over the dashboard's range (or its own) when the dashboard opens; its choices are the rows, so keep it to a stats or top.

Base searches and panels#

A base search is the shared part of a query, and its result is shared too: when two or more panels extend the same base over the same range, the dashboard runs the base once as a search job (JOBS.md) and each panel's own commands run over the job's result in the engine (POST /api/v1/jobs/{id}/search), so the dashboard costs one ClickHouse search per base rather than one per panel. The base strip says so ("ran once in 120 ms, 125 rows, sliced for 5 panels"), a sliced panel's header says "from the base", and the job is deleted once the panels are drawn.

A panel runs its own search instead, the base query with its commands appended, when sharing would save nothing or change what it shows:

  • a panel alone on its base: one search beats a job and a slice;
  • a panel whose commands do not start with a pipe, or cannot run in the engine, such as transaction (the slice answers 400 and the panel falls back to its own search);
  • every panel of a base that returns raw events when the event cap cut them. The base is asked for the host's cap of events (SEARCH-LIMITS.md); when more matched, the panels' aggregations would see part of the data, so each runs its own search, whose stats or timechart run in SQL over everything. A base that ends in a transforming command is complete and always shared; a base of raw events is shared while its events fit under the cap.

So keep a base to the filter and the first aggregation (index=web | bin _time span=1m | stats count by _time, status) and let the panels sum, pick and chart from there. Panels with the same query and range share one request either way. A dashboard runs its searches through a pool the size of the account's concurrent-search cap and waits out a busy answer, so it never trips the 429 by itself; it still counts against the host's cap while it runs, and a shared base is one job under the account's jobs cap while its panels are drawn.

How a panel reads its result:

Type What it draws
line, area one series per numeric column over _time (a timechart), or over the first text column; smooth, stacked, points, axis from zero
bar one series per numeric column by the first text column (stats count by host, top, chart ... over ... by ...); vertical or horizontal, stacked, value labels
pie the first text column as slices, the first numeric column as values; donut or full, rose, labels, slices kept before the rest fold into "other"
table every column, sortable by clicking a header; rows shown
gauge one number: the chosen field, or the first numeric column, of the first row (a stats) or of the newest complete bucket (a timechart); minimum, maximum, unit, decimals, amber and red thresholds, "lower is worse"
single the same number, large, with the change against the previous bucket and a sparkline when the result is a time series; the thresholds colour it

The category and value fields can be named instead of detected, and a unit and decimals apply to every chart. A timechart's newest bucket is still filling, so gauges and single values skip it by default ("Skip the newest, still-filling bucket").

API#

Method and path Who Purpose
GET /api/dashboards any list: id, title, description, owner, panel count, updated_at, can_edit
GET /api/dashboards/{id} any the dashboard and can_edit
POST /api/dashboards user create; the caller owns it; an empty id is made from the title
PUT /api/dashboards/{id} user replace own (admins: any); send the updated_at you loaded and a dashboard changed since answers 409 conflict
DELETE /api/dashboards/{id} user delete own (admins: any)

A dashboard:

{
  "id": "web", "title": "Web traffic", "description": "",
  "earliest": "-24h", "latest": "now", "refresh_s": 60,
  "inputs": [
    {"id": "host", "label": "Host", "type": "dropdown", "default": "*", "all": true,
     "search": "index=web | stats count by host", "value_field": "host", "label_field": ""},
    {"id": "status", "label": "Status", "type": "text", "default": ""}
  ],
  "searches": [{"id": "web", "query": "index=web sourcetype=nginx:access host=$host$ status=$status$", "earliest": "", "latest": ""}],
  "panels": [{
    "id": "status", "title": "By status", "type": "pie",
    "base": "web", "query": "| stats count by status",
    "earliest": "", "latest": "",
    "layout": {"x": 0, "y": 0, "w": 4, "h": 4},
    "options": {"donut": true, "drilldown": "token", "drilldown_token": "status"}
  }]
}

An input is text or dropdown; a dropdown has choices ([{"label": "Errors", "value": "error"}]) or a search with value_field and label_field, and all adds the All choice. A panel's options.drilldown is search (open the search page, the default), token (set the input drilldown_token to the clicked value) or none. The same document is what Export JSON writes and Import JSON reads; id, owner and the timestamps are dropped on import and a taken id gets a new one from the title.

type is one of pie, bar, line, area, table, gauge, single. With base, query is the commands appended to that base search (starting with a pipe, or empty for the base result as it is); without, it is the whole search. layout places the panel on a 12-column grid (x + w at most 12, h and y in rows). options are the visualization's settings as the UI writes them; the server only bounds their size. Every query is parsed and every time range checked on save; a refused dashboard answers 400 invalid with what is wrong. Limits: 12 inputs with 200 choices each, 20 base searches, 60 panels, 10,000 characters per query.

Dashboards live in dashboards.json under the engine's data directory and go into backups with the rest of it (BACKUP.md). Creating, changing and deleting one is audited (dashboard.create, dashboard.update, dashboard.delete).

Not yet#

  • Time and multi-select inputs, inputs scoped to one panel, and drilldown that targets another dashboard.
  • A base result kept between refreshes, or shared across dashboards: every run of a dashboard runs its bases again.
  • Searches run in UTC like the search page's: a dashboard cannot set a time zone for snaps such as @d.
  • Private dashboards, and a home dashboard per user.
  • Scheduled PDF or image reports, and embedding a panel elsewhere.
  • Further visualizations: scatter, heatmap, maps, a log-event list panel.