Ticket 044: `sequence` and `timestamp` collided with ordinary user-declared
param names and didn't signal they're ottyl-owned, so they're renamed
(breaking change, no back-compat alias) to `ottyl_seq`/`ottyl_ts` in the
filter grammar (src/filter_parser.pest, src/filter_parser.rs), alert query
restriction/evaluation (src/alerts.rs), and the filter-bar docs/placeholder
in the webapp.
Also adds a new reserved param `ottyl_fmt` exposing which format a line was
auto-detected as ("json"/"logfmt"/"none") via a new
`crate::logfmt::detect_format`, wired into the same filter grammar and alert
restriction list as `ottyl_seq`/`ottyl_ts`. A mixed-format fixture test in
engine.rs verifies `ottyl_fmt` reflects the actual detected format per line.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XFUNZwCvz3kcod3352ZR2o
50 lines
1.9 KiB
Plaintext
50 lines
1.9 KiB
Plaintext
// Grammar for the FilterBar query language.
|
|
//
|
|
// Supports:
|
|
// - regex("...") raw-text regex search over log lines
|
|
// - metric_name[param] <op> value metric comparisons
|
|
// - json[field] <op> value JSON field comparisons (line parsed as JSON)
|
|
// - ottyl_seq <op> value reserved metric name (no [param])
|
|
// - ottyl_ts <op> value reserved metric name (no [param])
|
|
// - ottyl_fmt <op> value reserved metric name (no [param])
|
|
// - `and` / `or` boolean combinators, with `and` binding tighter than `or`
|
|
// - parenthesized sub-expressions
|
|
|
|
WHITESPACE = _{ " " | "\t" | "\n" | "\r" }
|
|
|
|
file = { SOI ~ or_expr ~ EOI }
|
|
|
|
or_expr = { and_expr ~ (or_kw ~ and_expr)* }
|
|
and_expr = { term ~ (and_kw ~ term)* }
|
|
|
|
term = _{ regex_expr | comparison | "(" ~ or_expr ~ ")" }
|
|
|
|
// These must be atomic (`@`): a non-atomic rule would let pest's implicit
|
|
// whitespace insertion slip in between the keyword and its own boundary
|
|
// check below, which breaks the lookahead (it would see the first
|
|
// non-whitespace character *after* skipping the separating space, instead
|
|
// of the character immediately following the keyword).
|
|
and_kw = @{ "and" ~ !(ASCII_ALPHANUMERIC | "_") }
|
|
or_kw = @{ "or" ~ !(ASCII_ALPHANUMERIC | "_") }
|
|
|
|
regex_expr = { "regex" ~ "(" ~ string_literal ~ ")" }
|
|
|
|
string_literal = ${ "\"" ~ inner_str ~ "\"" }
|
|
inner_str = @{ (escaped_char | !("\"" | "\\") ~ ANY)* }
|
|
escaped_char = @{ "\\" ~ ANY }
|
|
|
|
comparison = _{ json_comparison | metric_comparison }
|
|
|
|
// `json[field] <op> value` must be tried before `metric_comparison` since "json" is
|
|
// otherwise a syntactically valid (if never declared) metric name.
|
|
json_comparison = { "json" ~ "[" ~ param ~ "]" ~ op ~ value }
|
|
metric_comparison = { metric ~ ("[" ~ param ~ "]")? ~ op ~ value }
|
|
|
|
metric = @{ ident }
|
|
param = @{ ident }
|
|
ident = @{ (ASCII_ALPHANUMERIC | "_")+ }
|
|
|
|
op = { "<=" | ">=" | "==" | "!=" | "<" | ">" }
|
|
|
|
value = @{ (!(WHITESPACE | ")") ~ ANY)+ }
|