Skip to content

Build and refine dashboards

Open chat and describe the system view you need. For example: “Build a dashboard of checkout latency and errors by service.” Fanout reads the telemetry schema, previews the panels and saves a dashboard. The save receipt records the saved version and reports panels that need attention. Open dashboard opens the current dashboard.

To refine the result, name the panel and the change: “On the checkout latency panel, add a warning threshold at 500 ms.” Use the receipt’s changes and version history to inspect what changed.

Chat needs a configured AI provider. When it is available, you can start in chat or use Create with AI on the empty dashboards page. Describe the question, services and time window you care about. Fanout discovers the available fields and values, previews its draft, and corrects invalid panels before saving. Missing telemetry should have an explanation in the panel description; an empty panel is not evidence that the system is healthy.

Expand Details in the receipt to see the stages, preview results, corrections and saved-panel checks. A saved version can still contain empty or failing panels. An incomplete save check means the panels were not all executed; it does not undo the save or establish that they work.

A follow-up edit reads the dashboard and changes the named panels, preserving the others. Ask explicitly for a redesign when you want the whole board replaced. You can also ask to add a variable, change the default time range or remove a panel.

The panel menu’s Explain in chat asks for an answer about the observed window, resolved variables and panel state. It cannot save dashboard changes. Ask Fanout to fix it is a separate edit request for a failed panel, available to the dashboard’s manager when chat is enabled. It asks Fanout to correct only that panel. All current account roles can manage their own dashboards; see roles.

The same panel types render in chat views and saved dashboards:

TypeUse it for
Stat (stat)One headline measure, with a trend and comparison when available.
Gauge (gauge)One measure against a defined minimum, maximum and thresholds.
Time series (timeseries)Change over time, as lines, areas or bars.
Bar (bar)Comparing categories; a deploy split can compare before and since a deploy.
Table (table)Ranked details and multiple measures, with unit, bar, status, sparkline and link formats.
Text (text)Markdown context and explanations.
Heatmap (heatmap)How a distribution, such as latency, changes over time.
Histogram (histogram)The distribution of span durations or metric histogram observations.
Scatter (scatter)The relationship between two measures per item, with optional colour grouping.
State timeline (state_timeline)Threshold states over time per item; missing values remain unknown.
Logs (logs)Individual redacted log events and their trace context.
Log patterns (log_patterns)Repeated redacted message templates, counts, trends, dominant severity and top service.
Traces (traces)Slow or erroring traces that you can open for evidence.
Service map (service_map)Service dependencies and traffic, latency and error context.
Health (health)Service health totals, error trend and health distribution.

Structured stat and gauge panels default to the measure over the whole selected window. A window p95 is computed from that window’s observations, not by averaging the trend’s bucket p95 values. Ask for a last, mean, minimum, maximum or sum reduction when that is the value you intend instead.

Units include milliseconds, seconds, nanoseconds, percent, ratio, count, rates per second or minute, bytes and unitless values. Structured measures carry their own units. Scatter can use different units on its two axes. Keep unrelated unit families in separate panels.

Log patterns group by message template. Their severity is the most frequent severity, with ties choosing the higher severity; their service is the most frequent service, with ties choosing the name that sorts first. These describe the matching redacted events, rather than splitting each template into extra severity or service groups.

Ask for a trace or log drill when you need evidence behind a chart. A configured panel can apply a variable filter when clicked, or open matching logs or exemplar traces. Deploy markers and anomaly bands provide context; they do not establish the cause of a change.

For queries that need it, a panel can use guarded SQL: one read-only SELECT over approved telemetry relations, with a required time-window filter. Logs, log patterns, traces, scatter and state timelines use structured queries. SQL is an authoring escape hatch. Spec is a read-only inspection view; there is no SQL or chart-style editor there.

get_telemetry_schema lists each metric’s name, unit, semantic type and temporality. The metrics view uses these kinds: gauge, counter (a monotonic sum), updown (a non-monotonic sum), histogram (explicit or exponential) and summary. These differ from the stored OTLP kinds: gauge, sum, histogram, exponential_histogram and summary. Discovery keeps separate entries when one name has different units, kinds or temporalities. There is no separate kind field in metric discovery.

Counters and histograms carry cumulative or delta temporality; gauges and summaries have empty temporality. start_time is the data point’s start time, or NULL when absent. Choose measures from the discovered kind:

Measuregauge, updowncounterhistogram
count()data pointsdata pointsdata points
rate()not definedΣ series (increase ÷ elapsed)Σ series (count increase ÷ elapsed)
sum(value)Σ series bucket averageΣ series increaseΣ sum increase
avg(value)mean of series bucket averagesnot definedΣ sum increase ÷ Σ count increase
min/max(value)over data pointsover data pointsnot defined
last(value)Σ series value at bucket endΣ series value at bucket endnot defined
pNN/quantile(value, q)over data pointsnot definedlinear interpolation over Σ bound increases

pNN means p50, p75, p90, p95 or p99. Undefined measures return NULL; an all-NULL panel is empty and explains which measures fit the kind. Summary metrics permit point-count operations but no value aggregates: stored summary quantiles cannot be combined. For every kind, count_distinct(field) counts distinct field values and share() is a group’s percentage of matching data points, rather than histogram observations or counter increases.

An aggregate metric query must fix the metric with a single-valued name equality filter in its own where entry, or include name in by. count() and count_distinct() are exempt; share() is not. For example:

{
"from": "metrics",
"where": ["name = 'system.memory.usage'", "service = $service"],
"measures": ["last(value)"]
}

Putting name = 'system.memory.usage' AND service = $service in one entry does not satisfy this rule. A name = $metric entry counts only when the variable is single-valued and has no All option. Multi-value variables and variables with include_all: true do not fix the name, even if the current selection is one metric: selecting All removes the filter. This is rejected:

{
"variables": [
{"name": "metric", "kind": "query", "from": "metrics", "field": "name", "include_all": true}
],
"panels": [
{
"id": "memory",
"title": "Memory",
"viz": "stat",
"query": {"from": "metrics", "where": ["name = $metric"], "measures": ["last(value)"]}
}
]
}

Remove the All option and keep the variable single-valued, or use a table or time series with "by": ["name"] to keep different metrics in separate groups.

Understand series, increases and elapsed time

Section titled “Understand series, increases and elapsed time”

A series combines namespace, service, name, unit, instrumentation scope name and version, attributes and resource. Fanout computes measures per series before combining series within a group. For gauges and updown metrics, sum(value) sums the series’ bucket averages and avg(value) averages those averages, so a series that exports more often does not get extra weight. If a series has no point in a bucket, these two measures use its latest earlier point when it is at most 5 minutes old at the clipped bucket start. They read a 5-minute lookback; older series stop contributing. min, max, percentiles and count() still use only points inside the selected window.

Counter and histogram increases read a 10-minute lookback before the selected window to find preceding points. A cumulative counter’s increase is current minus previous; a decrease or a change between two present start times is a reset and contributes the current value. Its first point without a predecessor is a baseline and contributes no increase. A delta point contributes its value, including its first point. For rates, a delta’s usable start time supplies the interval; otherwise a subsequent delta uses the previous point’s time. A first delta without a usable start time still contributes to sums but has no rate. Only positive elapsed intervals contribute to rates. Retries at the same timestamp are deduplicated before ordering and calculating increases.

Histograms apply increases to the count, sum and bucket weights together. A count decrease, changed present start times or an incompatible bucket layout resets all channels to their current values. Compatible layout changes can re-bin whole previous buckets; changes that lose observations or produce negative bucket differences reset instead. A decreasing histogram sum alone is not a reset, because observations can be negative.

Each cumulative increase covers the interval from its previous point to its current point. A delta uses its usable start time, or its preceding point’s time. For intervals of at most 10 minutes, Fanout spreads each increase across the buckets that interval overlaps, in proportion to the overlap, clipping buckets to the selected window. Longer intervals are gaps: the whole increase belongs to the current point’s bucket, with covered time equal to the full elapsed interval. Histogram count, sum and every bound’s increase follow the same rule.

rate() is per-second increase over covered elapsed time, not bucket width. For each series, Fanout divides the sum of its overlap shares by their covered seconds, then sums the series’ rates. Histogram rates use count increases. A counter exported every 60 seconds therefore contributes to all six 10-second buckets, and totals across machines with staggered or five-minute exports stay flat. In a bucket without a covering delta, rate() carries the latest completed delta’s rate when its end is at most 5 minutes before the clipped bucket start, including an end on that boundary. This keeps live charts level while reports are in flight. Sums, histogram averages and quantile weights receive no carried increases. Automatic metric buckets are at least 1 minute.

last(value) sums each series’ latest value strictly before the earlier of the bucket end and window end. A point exactly on a boundary belongs to the next bucket. Only series whose latest point is at most 5 minutes old at that end count; older series stop contributing. A whole-window stat therefore reflects recently reporting series rather than every series seen during the day.

Histogram percentiles come from summed bucket observation increases, with linear interpolation within the selected bucket, as in Prometheus. They do not take percentiles of the histogram point’s scalar value. The lowest bucket’s lower bound is 0 when its upper bound is positive; a percentile in the overflow bucket returns the highest finite bound.

These are approximations. In an exponential histogram’s zero bucket, interpolation spans (−threshold, threshold], so positive-only data can read slightly below 0, by at most the zero threshold (usually 0). Series with different bucket layouts in one group combine on the union of their bounds, which can lean a percentile upward by up to one coarse bucket width.

Use these panels when discovery reports system.network.io as a counter and http.server.request.duration as a histogram with seconds as its unit:

{
"name": "Metric measures",
"panels": [
{
"id": "network",
"title": "Network bytes per second",
"viz": "timeseries",
"query": {
"from": "metrics",
"where": ["name = 'system.network.io'"],
"measures": ["rate()"],
"by": ["attributes['direction']"],
"bucket": "auto"
}
},
{
"id": "duration",
"title": "Request duration p95",
"viz": "timeseries",
"query": {
"from": "metrics",
"where": ["name = 'http.server.request.duration'"],
"measures": ["p95(value)"],
"bucket": "auto"
},
"unit": "s"
}
]
}

Metric histogram and heatmap panels use the same per-series increases and read stored temporality. An authored histogram.temporality option is rejected; the panel cannot override the data’s meaning.

Use Data to inspect returned rows, execution time, interval and executed SQL when present. A failed panel’s full sanitized error appears in Data, even when the compact Chart preview cuts it short. On a narrow card, choose Data from the panel menu. Spec shows the authored panel definition. Text panels expose Content and Spec instead of Data.

Empty panels show their diagnosis in Chart. Check the time range, selected variables and the panel’s description before treating the absence as meaningful. Truncation and other result notes remain visible below the panel. If a refresh fails after data was loaded, the old data can remain on screen with a warning; it is not a fresh successful result. Use Retry for a request failure, or the separate fix action for an authored panel error.

The time picker offers relative ranges from five minutes through thirty days. For a fixed investigation, enter From and To under Absolute (your time zone) and choose Apply range. Zoom out widens the window.

Refresh now reloads the panels. Auto refresh offers Off, 10s, 30s, 1m and 5m; the default is 30s unless the dashboard specifies another interval. Refresh is paused while selecting a chart range.

Ask chat to add variables for filters you want to reuse:

KindBehaviour
QueryChoices discovered from telemetry in the selected window.
CustomA saved list of choices.
ConstantA saved read-only value.
TextFree-form input, applied on blur or Enter.

Query and custom variables can allow multiple selections. All removes filters that use that variable. Clearing a multi-select chooses All if it is enabled; otherwise None is an empty selection that matches no values. All and None have different meanings. A query variable can depend on an earlier variable, so selecting a service can narrow a later route selector. Options resolve in saved variable order and change with the window and dependencies.

Turn on Compare with previous period to compare with the preceding window of the same duration. Time series draw the previous series; stats display a previous value as a delta when available. Other panel types do not draw this comparison; a bar’s deploy split is a separate before/since view. An individual panel can have its own range or time shift; its header labels that override. Ask chat to set it when a panel needs a different window from the board.

Copy the address bar to share the current time, variables, comparison, focused panel and drill state. The panel menu’s Copy link preserves that state and opens the selected panel in full-screen, leaving layout edit mode out of the link. The auto-refresh selection is local to the page and is not carried in the URL.

Use an absolute range when the recipient needs the same time bounds. A relative range moves with the time the link is opened. Links open the current saved dashboard and query its data; they do not freeze a historical version or result.

The recipient needs an account and access to that dashboard. Dashboards are currently owner-scoped: another account cannot open your dashboard just because you copied its link. A view link grants no access and is not a public snapshot.

Choose Edit layout on a wide screen, drag panels by their titles and resize from a corner. Choose Save layout to persist the grid as a new version; Done leaves layout mode. Duplicate and Remove panel are available in panel menus while editing. Those actions save immediately, including any pending layout changes. Narrow screens stack panels and do not allow layout editing.

Edits use the version that was read to detect competing saves. If the board changed meanwhile, use Load latest, then redo the change. An MCP client should likewise read the latest dashboard and supply base_version when editing or replacing it.

Open History to inspect retained immutable versions, their author, message, changes and saved spec. Historical inspection does not query historical data. If the preceding version is no longer retained, the changes are unavailable but the selected spec can still be inspected.

Choose Restore version on an older entry to save that spec as a new version. For example, restoring v2 while v5 is current creates v6; it does not rewrite v2 or remove v3–v5. The dashboard reloads the restored panels. History and restore have the same owner scope as the dashboard.

Open Keyboard shortcuts for help and the Single-key shortcuts switch. The help uses these labels:

KeyAction
rRefresh now
eToggle layout mode
hOpen history
fOpen the focused panel in full-screen; Exit full-screen when already there
?Open keyboard shortcuts
EscapeCancel a range, then leave the chart or close full-screen
/Focus the chat composer

Focus a chart or panel control before pressing f; without a focused panel it does nothing. In full-screen, r, f and ? work; e and h are inactive. Dashboard shortcuts pause while typing, using menus or opening other dialogs and drawers. Repeated keys and modifier combinations are ignored, apart from Shift for ?. Turning off single-key shortcuts keeps visible controls and chart navigation available. The global / chat shortcut works outside text entry when chat is available; Escape in the composer clears it and leaves focus.

Tab directly to a chart. Left/Right moves between points; Up/Down changes series at the same time or category; Home/End goes to the first/last point. Enter drills or applies the configured panel filter when that point supports selection. Comparison and Other values can be read even when selection is unavailable.

On time charts, Shift+Left/Right extends a range and pauses auto-refresh. Review the shaded range and UTC label, then choose Zoom to range. Cancel or Escape cancels the selection and resumes refresh. The next Escape leaves the chart or closes full-screen. Use the time picker for precise edits after zooming; Reset zoom, Zoom out and browser Back preserve comparison and filters.

An external agent can use the same workflow after connecting over MCP. The generated MCP reference owns the full inputs and semantics.

ToolPurpose
get_telemetry_schemaDiscover signals, fields, common values, attributes, metrics and units.
preview_panelsValidate and execute a draft without saving; correct invalid panels and explain empty ones.
query_telemetryDisplay one executable panel as a chat view.
create_dashboardSave a complete dashboard for the authenticated user.
get_dashboard / list_dashboardsRead an owned dashboard and version, or list owned dashboards.
edit_dashboardApply typed operations atomically; unnamed panels stay unchanged.
replace_dashboardReplace the whole spec for a redesign; omitted panels are removed.
list_dashboard_versionsList retained versions, newest first, with author and message.
restore_dashboard_versionAppend a historical spec as a new version on explicit request.

All remote calls require telemetry:read. The dashboard tools, including their reads and version list, also require dashboard:manage, bound to your account’s own dashboards. These are delegated scopes, separate from the capability that allows Fanout’s built-in chat to run.

Apps-enabled clients additionally use query_panel_fragment, resolve_panel_variables and list_panel_exemplars to refresh complete views, resolve options and drill to traces. These three helpers are not offered to the model. There is no MCP dashboard deletion tool.