Rename reserved sequence/timestamp params to ottyl_seq/ottyl_ts; add ottyl_fmt

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
This commit is contained in:
Hugh Kaznowski
2026-09-06 01:50:16 +01:00
co-authored by Claude Sonnet 5
parent c84276937a
commit 534dc52e05
7 changed files with 179 additions and 41 deletions
+13 -8
View File
@@ -87,19 +87,23 @@ const SECTIONS: DocSection[] = [
<Code>{'regex("error")'}</Code> — lines containing "error".
</li>
<li>
<Code>{'regex("error") and sequence<5'}</Code> — lines containing "error" with sequence under 5.
<Code>{'regex("error") and ottyl_seq<5'}</Code> — lines containing "error" with sequence under 5.
</li>
<li>
<Code>{'requests[duration]>100'}</Code> — the <Code>requests</Code> metric's captured{' '}
<Code>duration</Code> param over 100.
</li>
<li>
<Code>{'timestamp>-5m'}</Code> — lines from the last 5 minutes.
<Code>{'ottyl_ts>-5m'}</Code> — lines from the last 5 minutes.
</li>
<li>
<Code>{'ottyl_fmt==json'}</Code> — lines auto-detected as JSON.
</li>
</ul>
<p>
<Code>sequence</Code> and <Code>timestamp</Code> are reserved metric names present on every log line, so
they're always filterable without declaring anything.
<Code>ottyl_seq</Code>, <Code>ottyl_ts</Code>, and <Code>ottyl_fmt</Code> are reserved metric names present
on every log line, so they're always filterable without declaring anything. <Code>ottyl_fmt</Code> holds the
auto-detected format of the line's text — <Code>json</Code>, <Code>logfmt</Code>, or <Code>none</Code>.
</p>
<p className="font-semibold text-text">Date/time formats</p>
<p>Timestamp comparisons accept three forms:</p>
@@ -121,7 +125,7 @@ const SECTIONS: DocSection[] = [
<ul className="list-disc pl-5">
<li>
Only lines mentioning "timeout" in the last 15 minutes:{' '}
<Code>{'regex("timeout") and timestamp>-15m'}</Code>
<Code>{'regex("timeout") and ottyl_ts>-15m'}</Code>
</li>
<li>
Requests slower than 200ms, once a <Code>requests</Code> metric has a <Code>value</Code>-role{' '}
@@ -134,7 +138,7 @@ const SECTIONS: DocSection[] = [
<Code>{'requests[path]==/checkout and regex("5[0-9]{2}")'}</Code>
</li>
<li>
Everything before noon on a specific date: <Code>{'timestamp<2024-06-15T12:00:00Z'}</Code>
Everything before noon on a specific date: <Code>{'ottyl_ts<2024-06-15T12:00:00Z'}</Code>
</li>
</ul>
</div>
@@ -367,8 +371,9 @@ const SECTIONS: DocSection[] = [
</ul>
<p>
Query-based conditions (Immediate/Threshold/Ratio) reuse the filter bar's syntax, restricted to{' '}
<Code>regex(...)</Code>, <Code>sequence</Code>, <Code>timestamp</Code>, and <Code>and</Code>/<Code>or</Code>
. A <Code>metric_name[param]</Code> comparison isn't supported in an alert query.
<Code>regex(...)</Code>, <Code>ottyl_seq</Code>, <Code>ottyl_ts</Code>, <Code>ottyl_fmt</Code>, and{' '}
<Code>and</Code>/<Code>or</Code>. A <Code>metric_name[param]</Code> comparison isn't supported in an alert
query.
</p>
<p>
A window accepts a plain number of milliseconds or a humantime-style duration: <Code>30s</Code>,{' '}
+1 -1
View File
@@ -35,7 +35,7 @@ export function FilterBar({
{collapseToggle}
<input
className={queryInputClassName}
placeholder='regex("error") and sequence<5 or timestamp>12:00'
placeholder='regex("error") and ottyl_seq<5 or ottyl_ts>12:00'
value={draft}
onChange={(e) => setDraft(e.target.value)}
/>