A better Datadog client for Emacs.
-
betterdatadog-show-dashboard— fetch a Datadog dashboard by id and render it in an Emacs buffer: title, description, and every widget (recursing into group widgets) with the metric queries that power it. -
Live graphs — betterdatadog fetches the last hour of data (via the Datadog query API) and draws a multi-row Unicode chart (scale-labelled, with a
lastvalue below it). When a widget defines a formula (e.g. an average rendered assum / count), the formula is evaluated point-by-point over its named queries and that result is charted — so you see the real value the widget represents, not the raw component series. Template variables (e.g.$model_name) are resolved from the dashboard's defaults before querying. Toggle withbetterdatadog-toggle-graphs; set the size withbetterdatadog-graph-heightandbetterdatadog-sparkline-width. -
Remembered dashboard ids — every id that fetches successfully is saved (with its title) to
betterdatadog-history-file, so next timebetterdatadog-show-dashboardoffers it for completion —TABto pick a remembered dashboard instead of pasting the id again. Set the variable tonilto disable persistence.
More to come (monitors, metric explorer).
betterdatadog has no external dependencies (it uses the built-in url.el and
json.el). Put betterdatadog.el on your load-path and:
(require 'betterdatadog)Set the site (region) for your account, and provide an API key + application key.
(setq betterdatadog-site "datadoghq.com") ;; or us3.datadoghq.com, datadoghq.eu, ...
(setq betterdatadog-api-key "...")
(setq betterdatadog-app-key "...")Prefer not to put keys in your init file? Leave them nil and store them via
auth-source (e.g. ~/.authinfo.gpg):
machine api.datadoghq.com login DD-API-KEY password <your-api-key>
machine api.datadoghq.com login DD-APPLICATION-KEY password <your-app-key>
(Use the host matching betterdatadog-site, prefixed with api..)
M-x betterdatadog-show-dashboard RET <dashboard-id> RET
The dashboard id is the slug in the dashboard URL, e.g. the abc-def-ghi in
https://app.datadoghq.com/dashboard/abc-def-ghi/.
Once an id has been fetched successfully it is remembered, so on later calls
you can hit TAB at the prompt and pick it (each candidate is annotated with
the dashboard title) instead of pasting the id again.
The dashboard buffer derives from special-mode, so the usual read-only
keys apply (q quits, g reverts/refreshes, SPC/DEL scroll).
betterdatadog ships no key bindings of its own — bind the commands however you like. The interactive commands are:
| Command | Action |
|---|---|
betterdatadog-refresh |
Re-fetch the board and graph data |
betterdatadog-toggle-graphs |
Toggle graphs on/off |
betterdatadog-toggle-queries |
Toggle the query strings on/off |
betterdatadog-set-window |
Set the graph time window (30m/4h…) |
betterdatadog-toggle-queries hides the metric query text for a clean,
graph-only view (betterdatadog-show-queries, default t). Toggling
queries or graphs re-renders instantly by reusing already-fetched data;
betterdatadog-refresh and betterdatadog-set-window discard the cache
and re-fetch.
Example bindings:
(with-eval-after-load 'betterdatadog
(define-key betterdatadog-mode-map (kbd "g") #'betterdatadog-refresh)
(define-key betterdatadog-mode-map (kbd "t") #'betterdatadog-toggle-graphs)
(define-key betterdatadog-mode-map (kbd "s") #'betterdatadog-toggle-queries)
(define-key betterdatadog-mode-map (kbd "w") #'betterdatadog-set-window))Evil's state keymaps shadow a major mode's own single-key bindings, so under Evil you'll want to bind into a state and/or mark the map as overriding yourself, e.g.:
(with-eval-after-load 'evil
(evil-set-initial-state 'betterdatadog-mode 'motion) ; keep j/k scrolling
(evil-define-key 'motion betterdatadog-mode-map
(kbd "t") #'betterdatadog-toggle-graphs
(kbd "s") #'betterdatadog-toggle-queries
(kbd "w") #'betterdatadog-set-window
(kbd "gr") #'betterdatadog-refresh))Graphs are controlled by betterdatadog-show-graphs (default t),
betterdatadog-graph-window-seconds (default 3600),
betterdatadog-graph-height (default 6), and
betterdatadog-sparkline-width (default 72).
Graph data is fetched in parallel before rendering: each distinct
query is requested once (duplicates across widgets are de-duplicated) and
all requests run concurrently, so a board's load time is roughly the
slowest single query rather than the sum of them all.
betterdatadog-fetch-timeout (default 30 seconds) bounds the wait;
any query that hasn't responded by then is fetched on demand as the
dashboard renders.
Each graph shows a rolling now − window … now range (default the last
hour), the same for every widget; the header line shows the current
window. Use betterdatadog-set-window and type a duration like 30m,
1h, 4h, 1d, or a bare number of seconds to change it for the
current buffer.
Web Service Overview
────────────────────
Key health metrics for the web tier.
id: abc-def-ghi layout: ordered widgets: 4
▸ Time to first token (avg) [timeseries]
sum:ttft_seconds.sum{*}.as_count()
sum:ttft_seconds.count{*}.as_count()
↳ query1 / query2
▂▃ ▄ ▃ ┈ 0.51
▃█████▆█▅▃
▂▅████████████▇▅▃▂ ┈ 0.08
last 0.45
▸ Errors [group]
▸ 5xx count [query_value]
sum:trace.http.request.errors{*}
last 2
▸ Top error endpoints [toplist]
top(sum:errors{*} by {resource}, 10, "sum", "desc")