ddquery-cli 0.1.1

A small command-line front-end over `ddquery-core`: parse a Datadog monitor query and print its explanation, structured summary, and AST.
ddquery-cli-0.1.1 is not a library.

build

ddquery

Parse and explain Datadog monitor queries in pure Rust.

A Datadog monitor query packs an aggregation window, a metric, a scope, a grouping, transforms, an evaluation function, and a threshold into one dense line:

avg(last_1d):anomalies(sum:app.service.latency{platform:macos, country:us}.as_count(), 'agile', 5, direction='below') >= 1

ddquery turns that string into a typed, serializable AST and renders it back as either a plain-language paragraph or a structured, render-ready breakdown:

Alert if the average over the 1 day of anomaly detection
(algorithm agile, ~5σ, direction below) (sum of `app.service.latency`
(platform:macos, country:us)) is ≥ 1.

Why a real parser

The query is a small but genuinely recursive language: an evaluation function nests over a rollup over a space-aggregation over a scoped metric, with arithmetic (a / b * 100), clamp_min(clamp_max(...)), and boolean composites (123 && !456) making it a tree, not a flat record. Regex extraction loses structure, mishandles nesting and quoting, and can't explain why a monitor fires. A grammar-driven parser is the correct tool, and the language is small and stable enough to own in-process — a hand-written recursive descent with a small Pratt loop for arithmetic precedence, no parser-generator dependency.

Features

  • Five dialects: metric (query/metric alert), search (logs/events/rum/error-tracking), service check, SLO error-budget, and boolean composite.
  • Never panics on input — every failure returns a ParseError with a byte offset and a one-line reason; fuzz-tested.
  • Graceful degradationparse_or_unparsed turns an unrecognized query into MonitorQuery::Unparsed { raw, reason, offset } so a consumer can always render the original verbatim, never dropping a record.
  • Bounded recursion — pathologically nested input is rejected before the stack grows.
  • Pure & fast — no I/O, no global state, linear in the input length.
  • Serde-ready — the AST and summary serialize to camelCase JSON for a front-end.

Usage

use ddquery_core::{explain, parse, summarize, MonitorQuery};

let query = "avg(last_5m):avg:system.cpu.user{env:production} > 90";
let ast = parse(query)?;

assert!(matches!(ast, MonitorQuery::Metric(_)));
println!("{}", explain(&ast));            // English paragraph
let summary = summarize(&ast, query);     // structured chips for a UI
# Ok::<(), ddquery_core::ParseError>(())

Command line

# pretty output
cargo run -p ddquery-cli -- 'error_budget("slo-99").over("30d") < 0.99'

# machine-readable bundle (ast + summary + explanation)
echo 'avg(last_5m):avg:system.cpu.user{env:prod} > 90' | cargo run -p ddquery-cli -- --json

Example

cargo run -p ddquery-core --example explain -- '<your query>'

Crate layout

Crate What
ddquery-core the library: AST, parser, explain/summarize
ddquery-cli a thin command-line front-end (the ddquery binary)

License

This project is distributed under the terms of the MIT license.

See LICENSE for details.

Copyright 2025 Tyr Chen