Building Dashboards and Notebooks
The idea in one sentence
Section titled “The idea in one sentence”A Datadog dashboard is a grid of widgets, each built from one or more named queries combined through a formula, and made interactive across the whole dashboard with template variables.
Widget types
Section titled “Widget types”Three widget types cover the vast majority of dashboards you’ll build:
- timeseries — a line or bar graph of a metric over time, the default choice for “how has this value trended.” Good for
system.cpu.user, request rate, error count, and so on. - query_value — a single big number, usually the latest value or an aggregation over a time window, used for at-a-glance KPIs like “current error rate” or “active connections right now.”
- group — a container widget that holds other widgets and can be scoped by a tag, letting you repeat the same set of graphs once per value of that tag — for example, one group per
service, each containing the same timeseries widgets, without manually duplicating them.
The formula and function model
Section titled “The formula and function model”Every widget query starts by naming a query — conventionally my_query_1, my_query_2, and so on — which is really just “this metric, filtered by these tags, aggregated this way.” The formula field is then a small expression that references those named queries by name, which lets you do two things a single raw metric query can’t:
- Arithmetic between two named queries — for example, computing an error rate as
my_query_1 / my_query_2 * 100wheremy_query_1is error count andmy_query_2is total request count. - Applying a rollup or anomaly function to one query — wrapping a single named query in a function like
rollup(my_query_1, "sum", 300)to force a 5-minute rolling sum, oranomalies(my_query_1, "basic")to highlight deviations from an expected band.
In plain-language terms, a widget config for an error-rate timeseries would describe: query my_query_1 as sum:myservice.errors.count{env:prod}.as_count(), query my_query_2 as sum:myservice.requests.count{env:prod}.as_count(), and a formula of (my_query_1 / my_query_2) * 100, displayed as a timeseries line. As a short illustrative HCL-like snippet (exact Terraform syntax is covered later in Production & Ecosystem):
widget "timeseries" { request { query { name = "my_query_1" query = "sum:myservice.errors.count{env:prod}.as_count()" } query { name = "my_query_2" query = "sum:myservice.requests.count{env:prod}.as_count()" } formula { formula_expression = "(my_query_1 / my_query_2) * 100" } }}Template variables
Section titled “Template variables”A template variable — say var_1, defaulting to a tag prefix like host — is a dashboard-level filter control that gets substituted into every widget’s query at once. Instead of hardcoding {env:prod} into every single widget, you define var_1 bound to the env tag, give it a default value, and every widget on the dashboard references {{var_1}} in its query scope. Change the dropdown at the top of the dashboard once, and every widget re-filters together. In practice, teams commonly scope a whole dashboard by env and service this way — one dashboard template, reused across every environment or every service, just by changing the variable selection at the top instead of building a new dashboard each time.
Notebooks: the lighter-weight alternative
Section titled “Notebooks: the lighter-weight alternative”A Notebook is a more freeform, document-style way to combine graphs with narrative text — you interleave timeseries and query_value cells with plain markdown paragraphs, in a linear top-to-bottom document rather than a fixed grid. Notebooks are the natural tool for a specific investigation or an incident writeup: “here’s the CPU graph during the spike, here’s what we changed, here’s the graph after the fix, here’s our conclusion” — a story with evidence embedded in it, rather than a reusable operational view you’d keep open in a TV dashboard. Dashboards are for ongoing monitoring; Notebooks are for one-time or periodic narrative analysis.
flowchart LR A[Named query my_query_1: errors] --> C[Formula: my_query_1 / my_query_2 * 100] B[Named query my_query_2: requests] --> C C --> D[Widget: timeseries / query_value / group] E[Template variable var_1 = env] -->|filters| D D --> F[Dashboard] D --> G[Notebook: graphs + narrative text]