Add worked examples to docs pages

Add concrete, verified examples to the General idea, Filtering,
Parameters, Metrics, Dashboards, and Settings docs sections, covering
common tasks like declaring a value-role duration param, building a
requests dashboard with line/bar/pie panels, and filter-bar syntax
gotchas (quoted vs. unquoted values, metric-only [param] comparisons).
Verified each example against the actual filter/PromQL parsers and
engine behavior in src/filter_parser.rs, src/promql.rs, and
src/engine.rs.

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-05 18:38:49 +01:00
co-authored by Claude Sonnet 5
parent 80515e0f84
commit 1db3adde90
+95
View File
@@ -62,6 +62,14 @@ const SECTIONS: DocSection[] = [
copy of the active one, and its "×" button deletes the active profile. Switching profiles hot-swaps the
running engine's declared params/metrics immediately, with no restart.
</p>
<p className="font-semibold text-text">Worked example</p>
<p>
Piping a webserver's access log — <Code>{'tail -f access.log | ottyl'}</Code> — into a profile named{' '}
<Code>webserver</Code>: create the profile from the settings menu, declare params/metrics against it (see
the <em>Parameters</em>, <em>Metrics</em>, and <em>Dashboards</em> sections below), then next time you run
the same command, switch to <Code>webserver</Code> and everything you declared is already there —
no re-declaring params or rebuilding dashboards.
</p>
</div>
),
},
@@ -109,6 +117,26 @@ const SECTIONS: DocSection[] = [
<p>
The filter string is evaluated on the server. A parse error shows as inline text under the filter bar.
</p>
<p className="font-semibold text-text">Worked examples</p>
<ul className="list-disc pl-5">
<li>
Only lines mentioning "timeout" in the last 15 minutes:{' '}
<Code>{'regex("timeout") and timestamp>-15m'}</Code>
</li>
<li>
Requests slower than 200ms, once a <Code>requests</Code> metric has a <Code>value</Code>-role{' '}
<Code>duration</Code> param declared: <Code>{'requests[duration]>200'}</Code>
</li>
<li>
5xx responses on one path, given a <Code>requests</Code> metric with a <Code>path</Code> label param —
note that <Code>[param]</Code> comparisons only work against a <em>declared metric's</em> labels, not a
bare param declared on its own, and values are unquoted (no surrounding <Code>"..."</Code>):{' '}
<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>
</li>
</ul>
</div>
),
},
@@ -141,6 +169,15 @@ const SECTIONS: DocSection[] = [
<Code>requests[duration]&gt;100</Code>), declare it as a param on a metric instead — see the Metrics
section below.
</p>
<p className="font-semibold text-text">Worked example</p>
<p>
Given lines like <Code>{'request path=/checkout method=GET duration=182'}</Code>, declaring a param named{' '}
<Code>duration</Code> with regex <Code>{String.raw`duration=(?P<duration>\d+)`}</Code>, capture group{' '}
<Code>duration</Code>, and type <Code>number</Code> makes <Code>duration</Code> show up as a column in the
log table for every line that matched, and comparable in the filter bar once it's also declared as part of
a metric (see <em>Filtering</em> above — bare params aren't queryable by name on their own, only metric
labels/values are).
</p>
</div>
),
},
@@ -235,6 +272,26 @@ const SECTIONS: DocSection[] = [
latest reading.
</li>
</ul>
<p className="font-semibold text-text">Worked example</p>
<p>
To count requests per path and track their latency from lines like{' '}
<Code>{'request path=/checkout method=GET duration=182'}</Code>, declare a metric named{' '}
<Code>requests</Code> with message-match regex{' '}
<Code>{String.raw`request path=(?P<path>\S+) method=(?P<method>\S+) duration=(?P<duration>\d+)`}</Code>{' '}
and two params: <Code>path</Code> (capture group <Code>path</Code>, kind <Code>string</Code>, role{' '}
<Code>label</Code>) and <Code>duration</Code> (capture group <Code>duration</Code>, kind{' '}
<Code>number</Code>, role <Code>value</Code> — at most one param can be <Code>value</Code>). Selecting{' '}
<Code>requests</Code> in the panel then shows one row per distinct <Code>path</Code> with its occurrence
count and a trend sparkline. The same declaration also unlocks{' '}
<Code>{'histogram(requests[50])'}</Code> (duration distribution in 50ms-wide buckets) and{' '}
<Code>gauge(requests)</Code> (each path's most recent duration) as dashboard panel queries.
</p>
<p>
A param of kind <Code>duration</Code> marked <Code>value</Code> is a special case: rather than parsing the
capture group as a number, its value is derived automatically as the elapsed time since the previous
matching event with the same labels — useful for measuring the gap between repeated events (e.g. time
between retries) without the log line itself reporting a duration.
</p>
</div>
),
},
@@ -314,6 +371,37 @@ const SECTIONS: DocSection[] = [
(default <Code>https://ntfy.sh</Code>). Configure the sink and send a test notification from the Settings
panel — the Alerts panel only defines rule conditions.
</p>
<p className="font-semibold text-text">Worked example: build a "requests" dashboard</p>
<p>
Assuming a <Code>requests</Code> metric declared as in the <em>Metrics</em> example above (a <Code>path</Code>{' '}
label and a <Code>duration</Code> value param), a useful dashboard is three panels:
</p>
<ul className="list-disc pl-5">
<li>
<span className="font-semibold text-text">Line</span>, query <Code>requests</Code> — one line per path,
showing every request occurrence over time.
</li>
<li>
<span className="font-semibold text-text">Bar</span>, query <Code>gauge(requests)</Code> — each path's
most recent request duration side by side, a quick "who's slow right now" snapshot.
</li>
<li>
<span className="font-semibold text-text">Pie</span>, query{' '}
<Code>{'requests{path="/checkout"}'}</Code>{' '}
— label matchers filter a query down to one series; here, just the checkout path's share of its own
occurrences over the range (mostly useful once more matchers are added to compare paths against each
other).
</li>
</ul>
<p>
Other query shapes worth knowing: <Code>{'sum(requests)'}</Code> collapses every path into one combined
count-over-time series, and <Code>{'rate(requests[5m])'}</Code> gives each path's occurrences-per-second
over a trailing 5-minute window — both work with any of the three chart types, since chart type is just how
the returned series get drawn, not part of the query itself. Add a panel, type a query, and it previews
live; a malformed query (e.g. mismatched braces, or <Code>histogram()</Code> missing its{' '}
<Code>[width]</Code>) shows its error inline in the panel instead of breaking the dashboard — a syntactically
valid query for a metric that doesn't exist just renders "No data in range."
</p>
</div>
),
},
@@ -327,6 +415,13 @@ const SECTIONS: DocSection[] = [
max number of logs kept in memory (blank means unlimited), and imported/linked CSS. Changes apply
immediately and persist to the settings file on disk. There is no save button.
</p>
<p className="font-semibold text-text">Worked example</p>
<p>
Ingesting a very chatty process and only care about the last hour of activity: set{' '}
<em>max logs in memory</em> to something like <Code>50000</Code> so older lines are dropped as new ones
arrive rather than growing memory use unbounded for the life of the process. Leave it blank to keep
everything ingested — the default, and fine for shorter-lived commands.
</p>
</div>
),
},