diff --git a/webapp/src/components/Docs.tsx b/webapp/src/components/Docs.tsx index 9ab625c..656a250 100644 --- a/webapp/src/components/Docs.tsx +++ b/webapp/src/components/Docs.tsx @@ -18,43 +18,49 @@ const SECTIONS: DocSection[] = [ title: 'General idea', body: (
-

- ottyl turns a CLI program's stdout into a Grafana-like dashboard. Pipe a program's output into it, and it - indexes each line in memory as it arrives while serving a dashboard from the same process. -

+

ottyl turns a CLI program's stdout into a dashboard. Pipe a program's output into it:

{'cli_command --verbose | ottyl'}

- Everything — log lines, params, metrics, and derived buckets — lives in one in-memory index for the - process's lifetime. Nothing touches disk unless you save a profile or change a setting. + It indexes each line in memory as it arrives and serves the dashboard from the same process. Everything — + log lines, params, metrics, and derived buckets — lives in that in-memory index for the process's + lifetime. Nothing touches disk unless you save a profile or change a setting.

CLI arguments

Settings

- There is one settings file per machine, holding the colour scheme, any imported/linked CSS, and + One settings file per machine holds the colour scheme, imported/linked CSS, the ntfy sink, and which profile is active. Settings only tracks profile pointers — the profiles themselves (params, metrics, dashboards) are stored separately, next to the settings file by default.

Profiles

A profile bundles the parsing/indexing rules for one CLI app — its params and metrics, plus any saved - dashboards — so restarting ottyl against the same program doesn't lose your setup. A Default{' '} - profile always exists and can't be deleted. Creating a new profile copies the active one (or starts empty - if none is active), so variations build on what you already have. Switching profiles hot-swaps the running - engine's declared params/metrics immediately, with no restart needed. + dashboards — so restarting ottyl against the same program keeps your setup. The Default{' '} + profile always exists and can't be deleted. The profile dropdown's "+" button creates a new profile as a + 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.

), @@ -65,30 +71,43 @@ const SECTIONS: DocSection[] = [ body: (

- The filter bar takes a query combining regex search over raw log text with comparisons against declared - metrics, joined by and/or: + The filter bar combines regex search over raw log text with comparisons against declared metrics, joined + by and/or. Examples:

-

- {'regex("error") and sequence<5 or timestamp>12:00'} -

-

- {'metric_name[param]<5'} compares a metric's captured param against a number, string, or - timestamp. sequence and timestamp are reserved metric names present on every log - line, so they're always filterable without declaring anything. -

-

Date/time formats

-

Timestamp comparisons accept several forms:

- The same filter string goes to both GET /api/logs and GET /api/timeline as the{' '} - filter query param and is evaluated on the server; a parse error surfaces as inline text under - the filter bar rather than crashing the page. + sequence and timestamp are reserved metric names present on every log line, so + they're always filterable without declaring anything. +

+

Date/time formats

+

Timestamp comparisons accept three forms:

+ +

+ The filter string is evaluated on the server. A parse error shows as inline text under the filter bar.

), @@ -99,20 +118,28 @@ const SECTIONS: DocSection[] = [ body: (

- A parameter declaration tells ottyl how to derive a named value from a log line: a regex the message must - match, a capture group, and a type (string, number, or timestamp). - Declaring one retroactively indexes every already-ingested line and checks every new line as it arrives. + A parameter declaration derives a named value from a log line: a regex the message must match, a capture + group, and a type (string, number, or timestamp). Example:

+

- Params are the building blocks metrics are made of (a metric is really a message-match regex plus a set of - param matchers), and they drive filtering and the log preview's column picker — declaring a{' '} - duration param, for instance, makes it available as both a log-table column and a comparable - value in the filter bar. -

-

- The Params panel declares a bare param directly (POST /api/params) — the same request the - Metrics panel's declaration form makes per param matcher. Declaring one there updates the log preview's - column picker immediately, with no page reload needed. + Declaring a param indexes every already-ingested line immediately, then checks every new line as it + arrives. It shows up right away as a column in the log table. To filter on it (e.g.{' '} + requests[duration]>100), declare it as a param on a metric instead — see the Metrics + section below.

), @@ -127,11 +154,10 @@ const SECTIONS: DocSection[] = [ select a fixed range, narrowing the log table, metrics, and dashboards below to that window.

- The preset buttons (All, 5m, 15m, 1h,{' '} - 24h) are Grafana-style shortcuts: All clears the range entirely, and a relative - preset is rolling — its window keeps advancing to stay "last N minutes from now" instead of - freezing at selection time. Dragging while a preset is active switches to a fixed (non-rolling) range, same - as a normal drag. + The preset buttons — All, 1m, 2m, 5m, 15m,{' '} + 1h, 24h — are quick-range shortcuts. All clears the range. Every + other preset is rolling: its window keeps advancing to stay "last N minutes from now" instead of + freezing at selection time. Dragging while a preset is active switches to a fixed range.

), @@ -144,7 +170,11 @@ const SECTIONS: DocSection[] = [

The log table lists ingested lines newest-last, each with its sequence number, timestamp, and raw text. The column picker (top-right) adds a column per declared param, showing that line's derived value (blank if it - didn't match) — a quick way to eyeball params without switching to the Metrics panel. + didn't match). +

+

+ Scrolling stays anchored to the row you're looking at as new lines arrive or old ones age out of a rolling + window — the view doesn't jump when content is pruned from the top.

), @@ -155,16 +185,29 @@ const SECTIONS: DocSection[] = [ body: (

- Every metric is fundamentally a counter: a message-match regex paired with one or more named param - matchers (each with its own capture group and type). Every log line matching the regex is one counter - increment — a timestamped occurrence, tagged with the labels captured from that line. + Every metric is a counter: a message-match regex paired with one or more named param matchers + (each with its own capture group and type). Every log line matching the regex is one counter increment — a + timestamped occurrence, tagged with the labels captured from that line. Example:

+

Each param is either a label (the default) or the metric's single value{' '} - param. Labels identify which bucket an occurrence belongs to; a value param's captured number becomes the - occurrence's stored value instead of a label — e.g. a duration param marked value{' '} - lets you query the distribution or latest reading, not just how often it occurred. A metric with only - label params defaults every occurrence's value to a fixed 1 (a pure occurrence count). + param. Labels identify which bucket an occurrence belongs to. A value param's captured number becomes the + occurrence's stored value instead of a label — in the example above, duration lets you query + the distribution or latest reading, not just how often the line occurred. A metric with only label params + defaults every occurrence's value to a fixed 1 (a pure occurrence count).

Declaring a metric backfills it against everything already ingested, then keeps indexing new lines as they @@ -232,39 +275,44 @@ const SECTIONS: DocSection[] = [

An alert rule fires a notification when its condition matches a newly-ingested log line, evaluated per line - as it arrives rather than backfilled against history. There are four condition types: + as it arrives. There are four condition types:

- Query-based conditions (Immediate/Threshold/Ratio) reuse the filter bar's syntax, but only the subset that - evaluates a single line's own fields — regex(...), sequence,{' '} - timestamp, and and/or. A metric_name[param] comparison - isn't supported in an alert query. + Query-based conditions (Immediate/Threshold/Ratio) reuse the filter bar's syntax, restricted to{' '} + regex(...), sequence, timestamp, and and/or + . A metric_name[param] comparison isn't supported in an alert query.

- A window accepts a plain number of milliseconds or a humantime-style duration like 30s,{' '} + A window accepts a plain number of milliseconds or a humantime-style duration: 30s,{' '} 5m, 1h.

- Every rule delivers to an ntfy sink: a topic (required) and an optional server override (default{' '} - https://ntfy.sh). "Send test notification" delivers to the form's current topic/server without - needing to save the rule first. + Every rule delivers to the single ntfy sink configured in Settings: a topic and an optional server override + (default https://ntfy.sh). Configure the sink and send a test notification from the Settings + panel — the Alerts panel only defines rule conditions.

), @@ -275,9 +323,9 @@ const SECTIONS: DocSection[] = [ body: (

- The Settings panel controls process-wide state: colour scheme, the 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 — no save button needed. + The Settings panel controls process-wide state: colour scheme, the ntfy sink alert rules deliver to, the + 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.

), @@ -288,9 +336,11 @@ const SECTIONS: DocSection[] = [ body: (

- The Presets panel manages profiles: create, switch, and delete them. Switching activates a profile's - params/metrics in the running engine immediately, no restart needed — see General idea above for - what a profile stores. + The profile dropdown in the sidebar manages profiles. Selecting one switches to it and activates its + params/metrics in the running engine immediately, with no restart. The + button prompts for a + name and creates a new profile as a copy of the active one, then switches to it. The × button + deletes the active profile; it's disabled for Default, which can't be deleted. See{' '} + General idea above for what a profile stores.

),