monitor.parsers¶
Metric parsers — convert raw command output (CommandResult.value) to numeric values.
Built-in parsers cover the most common Linux host metrics. To add support for a custom command, subclass MetricParser and override parse():
class MyAppParser(MetricParser):
chart = "Connections"
unit = ""
command = "ss -s | grep estab"
def parse(self, output: str, *, ctx: ParseContext) -> dict[str, MetricDataPoint]: ...
To associate custom parsers with a specific host, call register_host_parsers() from an init module listed in .otto/settings.toml:
from otto.monitor.parsers import DEFAULT_PARSERS, TopCpuParser, register_host_parsers
from my_repo.parsers import NvidiaGpuParser
register_host_parsers(
"gpu-01",
{
**DEFAULT_PARSERS,
"nvidia-smi --query-gpu=utilization.gpu ...": NvidiaGpuParser(),
},
)
Hosts with no registered parsers fall back to DEFAULT_PARSERS.
To add or override a parser for every monitored host instead of one host, call register_parsers() the same way:
from otto.monitor.parsers import register_parsers
from my_repo.parsers import UptimeParser
register_parsers([UptimeParser()])
Project-level entries merge over DEFAULT_PARSERS by command string (a command matching a default overrides it; a new command extends the set), and yield to any per-host registration for that host.
-
class otto.monitor.parsers.MetricDataPoint(value: float, meta: dict[str, Any] | None =
None)¶ Bases:
NamedTupleA single data point returned by MetricParser.parse().
-
class otto.monitor.parsers.ParseContext(core_count: int =
1, ts: datetime | None =None)¶ Bases:
objectTick-local input to MetricParser.parse — extensible without signature breaks.
-
core_count : int =
1¶ Number of CPU cores on the target host for this tick. Most parsers ignore this;
TopCpuParseruses it to normalize per-process CPU%.
-
ts : datetime | None =
None¶ Collection timestamp for this tick. Rate parsers feed it to their
RateTracker;None(bare construction in tests) means the parser falls back todatetime.now(tz=timezone.utc).
-
core_count : int =
- class otto.monitor.parsers.TimedSample(ts: datetime | None, series: dict[str, MetricDataPoint])¶
Bases:
NamedTupleOne timestamped batch of series points produced by
MetricParser.parse_tick().- ts : datetime | None¶
Data-carried timestamp for every point in
series;Nonemeans “stamp with the collector’s tick time” (what plainparse()output gets).
- series : dict[str, MetricDataPoint]¶
Series label → data point, exactly as
parse()returns.
- class otto.monitor.parsers.LogEvent(ts: datetime, fields: dict[str, str])¶
Bases:
NamedTupleOne columnar log-event row produced by
MetricParser.parse_tick().A table row, not a chart point — rendered by
kind="table"dashboard tabs. Deliberately separate fromMonitorEvent(the global, low-volume chart-marker annotation system): log events are per-host, high-volume, columnar data.
- class otto.monitor.parsers.TickResult(samples: list[TimedSample], events: list[LogEvent])¶
Bases:
NamedTupleEverything a parser produced for one tick: timed samples and/or log events.
- samples : list[TimedSample]¶
Alias for field number 0
-
otto.monitor.parsers.human_readable(value: float, precision: int =
1) str¶ Format a byte count as a human-readable string using binary prefixes (df -h style).
- Parameters:¶
value – The value in bytes.
precision – Maximum number of decimal places in the output (trailing zeros are stripped).
>>> human_readable(0) '0 B' >>> human_readable(1024) '1 K' >>> human_readable(1536) '1.5 K' >>> human_readable(1073741824) '1 G'
- class otto.monitor.parsers.MetricParser¶
Bases:
ABCBase class for metric parsers.
Subclass this and set the class attributes, then override parse() to extract a single numeric value from the raw command output string.
- unit : str¶
Unit suffix for chart annotations, e.g. ‘%’, ‘MB’, ‘GB’. Empty string for dimensionless values.
-
tab_label : str =
'Metrics'¶ Human-readable label for the tab button, e.g. ‘CPU’. Defaults to ‘Metrics’.
- chart : str¶
Chart group id. Series with the same chart value share one Plotly chart. Single-series parsers set this to their series label; multi-series parsers set it to a shared group name (e.g.
'Load').
-
interval : float | None =
None¶ Collection interval override in seconds for this parser’s command.
Nonemeans the collector’s global--interval(the default). Honored byrun()’s per-interval scheduling.
-
table_columns : list[str] | None =
None¶ Table columns for log-event parsers — their tab renders as a table (
TabSpec.kind == "table") instead of charts.None(the default) for chart parsers. Table parsers must declare their owntabid; sharing a tab with chart parsers raises inget_meta_model().
- abstract parse(output: str, *, ctx: ParseContext) dict[str, MetricDataPoint]¶
Convert raw command output into one or more labelled data points.
- Parameters:¶
output – The full stdout+stderr string returned by the remote command.
ctx – Tick-local input (e.g. the target host’s core count).
- Returns:¶
A dict mapping series label →
MetricDataPointfor each data point produced this tick. Return an empty dict if parsing fails. Single-series parsers return{self.chart: MetricDataPoint(value)}. Multi-series parsers may return multiple entries in the dict. Log-sourced parsers — which overrideparse_tick()— implement this as a trivialreturn {}.
- parse_tick(output: str, *, ctx: ParseContext) TickResult¶
Convert one tick’s command output into timed samples and/or log events.
The collector always calls this, never
parse()directly. The default wrapsparse()as a single untimed sample, so existing parsers behave exactly as before. Log-sourced parsers (seeotto.monitor.log_sourced) override this to emit data-carried timestamps — multiple samples per read, ascending — and/or columnarLogEventrows.
-
class otto.monitor.parsers.TopCpuParser(top_n: int =
5, delay: float =0.5)¶ Bases:
MetricParserParse overall and per-process CPU usage from
top -d{delay} -bn2output.Runs two top iterations separated by delay seconds so that per-process %CPU values reflect activity during that interval (the first iteration has no baseline and is discarded). Overall CPU usage and per-process traces share one chart.
- Parameters:¶
top_n – Maximum number of processes to include per collection tick.
delay – Seconds between top iterations (controls accuracy vs latency trade-off).
-
unit : str =
'%'¶ Unit suffix for chart annotations, e.g. ‘%’, ‘MB’, ‘GB’. Empty string for dimensionless values.
-
tab_label : str =
'CPU'¶ Human-readable label for the tab button, e.g. ‘CPU’. Defaults to ‘Metrics’.
-
chart : str =
'CPU'¶ Chart group id. Series with the same chart value share one Plotly chart. Single-series parsers set this to their series label; multi-series parsers set it to a shared group name (e.g.
'Load').
- parse(output: str, *, ctx: ParseContext) dict[str, MetricDataPoint]¶
Convert raw command output into one or more labelled data points.
- Parameters:¶
output – The full stdout+stderr string returned by the remote command.
ctx – Tick-local input (e.g. the target host’s core count).
- Returns:¶
A dict mapping series label →
MetricDataPointfor each data point produced this tick. Return an empty dict if parsing fails. Single-series parsers return{self.chart: MetricDataPoint(value)}. Multi-series parsers may return multiple entries in the dict. Log-sourced parsers — which overrideparse_tick()— implement this as a trivialreturn {}.
- class otto.monitor.parsers.MemParser¶
Bases:
MetricParserParse memory and swap usage % from free -b output.
Reads the ‘Mem:’ and ‘Swap:’ lines and computes used/total as percentages.
-
unit : str =
'%'¶ Unit suffix for chart annotations, e.g. ‘%’, ‘MB’, ‘GB’. Empty string for dimensionless values.
-
tab_label : str =
'Memory'¶ Human-readable label for the tab button, e.g. ‘CPU’. Defaults to ‘Metrics’.
-
chart : str =
'Memory Usage'¶ Chart group id. Series with the same chart value share one Plotly chart. Single-series parsers set this to their series label; multi-series parsers set it to a shared group name (e.g.
'Load').
- parse(output: str, *, ctx: ParseContext) dict[str, MetricDataPoint]¶
Convert raw command output into one or more labelled data points.
- Parameters:¶
output – The full stdout+stderr string returned by the remote command.
ctx – Tick-local input (e.g. the target host’s core count).
- Returns:¶
A dict mapping series label →
MetricDataPointfor each data point produced this tick. Return an empty dict if parsing fails. Single-series parsers return{self.chart: MetricDataPoint(value)}. Multi-series parsers may return multiple entries in the dict. Log-sourced parsers — which overrideparse_tick()— implement this as a trivialreturn {}.
-
unit : str =
- class otto.monitor.parsers.DiskParser¶
Bases:
MetricParserParse root filesystem usage % from df -h / output.
Reads the data row (second line) and extracts the Use% column. parse_meta() returns the already human-readable Size/Used strings from df -h.
-
unit : str =
'%'¶ Unit suffix for chart annotations, e.g. ‘%’, ‘MB’, ‘GB’. Empty string for dimensionless values.
-
tab_label : str =
'Disk'¶ Human-readable label for the tab button, e.g. ‘CPU’. Defaults to ‘Metrics’.
-
chart : str =
'Disk Usage'¶ Chart group id. Series with the same chart value share one Plotly chart. Single-series parsers set this to their series label; multi-series parsers set it to a shared group name (e.g.
'Load').
- parse(output: str, *, ctx: ParseContext) dict[str, MetricDataPoint]¶
Convert raw command output into one or more labelled data points.
- Parameters:¶
output – The full stdout+stderr string returned by the remote command.
ctx – Tick-local input (e.g. the target host’s core count).
- Returns:¶
A dict mapping series label →
MetricDataPointfor each data point produced this tick. Return an empty dict if parsing fails. Single-series parsers return{self.chart: MetricDataPoint(value)}. Multi-series parsers may return multiple entries in the dict. Log-sourced parsers — which overrideparse_tick()— implement this as a trivialreturn {}.
-
unit : str =
- class otto.monitor.parsers.LoadParser¶
Bases:
MetricParserParse all three load averages from cat /proc/loadavg.
Output format: ‘0.52 0.58 0.59 1/432 12345’ Returns 1-minute, 5-minute, and 15-minute load averages as separate series.
-
unit : str =
''¶ Unit suffix for chart annotations, e.g. ‘%’, ‘MB’, ‘GB’. Empty string for dimensionless values.
-
tab_label : str =
'CPU'¶ Human-readable label for the tab button, e.g. ‘CPU’. Defaults to ‘Metrics’.
-
chart : str =
'Load'¶ Chart group id. Series with the same chart value share one Plotly chart. Single-series parsers set this to their series label; multi-series parsers set it to a shared group name (e.g.
'Load').
- parse(output: str, *, ctx: ParseContext) dict[str, MetricDataPoint]¶
Convert raw command output into one or more labelled data points.
- Parameters:¶
output – The full stdout+stderr string returned by the remote command.
ctx – Tick-local input (e.g. the target host’s core count).
- Returns:¶
A dict mapping series label →
MetricDataPointfor each data point produced this tick. Return an empty dict if parsing fails. Single-series parsers return{self.chart: MetricDataPoint(value)}. Multi-series parsers may return multiple entries in the dict. Log-sourced parsers — which overrideparse_tick()— implement this as a trivialreturn {}.
-
unit : str =
- class otto.monitor.parsers.NetDevParser¶
Bases:
MetricParserPer-interface network throughput from
/proc/net/devcounter deltas.Emits
rx <iface>/tx <iface>byte rates; packet/error/drop rates ride each series’ hover meta. The loopback interface is skipped. First tick per interface is the rate baseline and emits nothing; a counter reset (reboot) skips one tick and re-baselines (seeotto.monitor.rates).-
unit : str =
'B/s'¶ Unit suffix for chart annotations, e.g. ‘%’, ‘MB’, ‘GB’. Empty string for dimensionless values.
-
tab_label : str =
'Network'¶ Human-readable label for the tab button, e.g. ‘CPU’. Defaults to ‘Metrics’.
-
chart : str =
'Network I/O'¶ Chart group id. Series with the same chart value share one Plotly chart. Single-series parsers set this to their series label; multi-series parsers set it to a shared group name (e.g.
'Load').
- parse(output: str, *, ctx: ParseContext) dict[str, MetricDataPoint]¶
Convert raw command output into one or more labelled data points.
- Parameters:¶
output – The full stdout+stderr string returned by the remote command.
ctx – Tick-local input (e.g. the target host’s core count).
- Returns:¶
A dict mapping series label →
MetricDataPointfor each data point produced this tick. Return an empty dict if parsing fails. Single-series parsers return{self.chart: MetricDataPoint(value)}. Multi-series parsers may return multiple entries in the dict. Log-sourced parsers — which overrideparse_tick()— implement this as a trivialreturn {}.
-
unit : str =
- class otto.monitor.parsers.SocketsParser¶
Bases:
MetricParserTCP socket-state counts from the
TCP:summary line ofss -s.Hosts without
ssproduce a shell error the parser cannot match — the series simply never appears (and the collector warns once; see parser-health warnings). Swap in anetstat-based parser per host viaregister_host_parsers()if needed.-
unit : str =
''¶ Unit suffix for chart annotations, e.g. ‘%’, ‘MB’, ‘GB’. Empty string for dimensionless values.
-
tab_label : str =
'Network'¶ Human-readable label for the tab button, e.g. ‘CPU’. Defaults to ‘Metrics’.
-
chart : str =
'Sockets'¶ Chart group id. Series with the same chart value share one Plotly chart. Single-series parsers set this to their series label; multi-series parsers set it to a shared group name (e.g.
'Load').
- parse(output: str, *, ctx: ParseContext) dict[str, MetricDataPoint]¶
Convert raw command output into one or more labelled data points.
- Parameters:¶
output – The full stdout+stderr string returned by the remote command.
ctx – Tick-local input (e.g. the target host’s core count).
- Returns:¶
A dict mapping series label →
MetricDataPointfor each data point produced this tick. Return an empty dict if parsing fails. Single-series parsers return{self.chart: MetricDataPoint(value)}. Multi-series parsers may return multiple entries in the dict. Log-sourced parsers — which overrideparse_tick()— implement this as a trivialreturn {}.
-
unit : str =
- class otto.monitor.parsers.DiskIoParser¶
Bases:
MetricParserPer-device read/write throughput from
/proc/diskstatssector deltas.Whole devices only: partitions (
sda1,nvme0n1p2,mmcblk0p1) and virtual/noise devices (loop*,ram*,dm-*,zram*,sr*) are skipped so charts show physical disk activity once.-
unit : str =
'B/s'¶ Unit suffix for chart annotations, e.g. ‘%’, ‘MB’, ‘GB’. Empty string for dimensionless values.
-
tab_label : str =
'Disk'¶ Human-readable label for the tab button, e.g. ‘CPU’. Defaults to ‘Metrics’.
-
chart : str =
'Disk I/O'¶ Chart group id. Series with the same chart value share one Plotly chart. Single-series parsers set this to their series label; multi-series parsers set it to a shared group name (e.g.
'Load').
- parse(output: str, *, ctx: ParseContext) dict[str, MetricDataPoint]¶
Convert raw command output into one or more labelled data points.
- Parameters:¶
output – The full stdout+stderr string returned by the remote command.
ctx – Tick-local input (e.g. the target host’s core count).
- Returns:¶
A dict mapping series label →
MetricDataPointfor each data point produced this tick. Return an empty dict if parsing fails. Single-series parsers return{self.chart: MetricDataPoint(value)}. Multi-series parsers may return multiple entries in the dict. Log-sourced parsers — which overrideparse_tick()— implement this as a trivialreturn {}.
-
unit : str =
- class otto.monitor.parsers.PerCoreCpuParser¶
Bases:
MetricParserPer-core busy % from
/proc/statjiffies deltas.Far cheaper than a second
toprun: busy% = 100 x (1 - Δ(idle+iowait) / Δtotal) percpuNline. The aggregatecpuline is skipped —TopCpuParseralready charts overall CPU. Jiffies ratios need no wall clock (time cancels), so state is plain previous counters.-
unit : str =
'%'¶ Unit suffix for chart annotations, e.g. ‘%’, ‘MB’, ‘GB’. Empty string for dimensionless values.
-
tab_label : str =
'CPU'¶ Human-readable label for the tab button, e.g. ‘CPU’. Defaults to ‘Metrics’.
-
chart : str =
'Per-core CPU'¶ Chart group id. Series with the same chart value share one Plotly chart. Single-series parsers set this to their series label; multi-series parsers set it to a shared group name (e.g.
'Load').
- parse(output: str, *, ctx: ParseContext) dict[str, MetricDataPoint]¶
Convert raw command output into one or more labelled data points.
- Parameters:¶
output – The full stdout+stderr string returned by the remote command.
ctx – Tick-local input (e.g. the target host’s core count).
- Returns:¶
A dict mapping series label →
MetricDataPointfor each data point produced this tick. Return an empty dict if parsing fails. Single-series parsers return{self.chart: MetricDataPoint(value)}. Multi-series parsers may return multiple entries in the dict. Log-sourced parsers — which overrideparse_tick()— implement this as a trivialreturn {}.
-
unit : str =
- class otto.monitor.parsers.ProcCountParser¶
Bases:
MetricParserProcess counts: runnable/total from loadavg field 4, blocked from /proc/stat.
Cats both files in one command — the command string doubles as the parser registry key, so it must differ from
LoadParser’scat /proc/loadavgandPerCoreCpuParser’scat /proc/stat; reading both also getsprocs_blockedfor free.-
unit : str =
''¶ Unit suffix for chart annotations, e.g. ‘%’, ‘MB’, ‘GB’. Empty string for dimensionless values.
-
command : str =
'cat /proc/loadavg /proc/stat'¶ The exact shell command whose output this parser handles.
-
tab_label : str =
'CPU'¶ Human-readable label for the tab button, e.g. ‘CPU’. Defaults to ‘Metrics’.
-
chart : str =
'Processes'¶ Chart group id. Series with the same chart value share one Plotly chart. Single-series parsers set this to their series label; multi-series parsers set it to a shared group name (e.g.
'Load').
- parse(output: str, *, ctx: ParseContext) dict[str, MetricDataPoint]¶
Convert raw command output into one or more labelled data points.
- Parameters:¶
output – The full stdout+stderr string returned by the remote command.
ctx – Tick-local input (e.g. the target host’s core count).
- Returns:¶
A dict mapping series label →
MetricDataPointfor each data point produced this tick. Return an empty dict if parsing fails. Single-series parsers return{self.chart: MetricDataPoint(value)}. Multi-series parsers may return multiple entries in the dict. Log-sourced parsers — which overrideparse_tick()— implement this as a trivialreturn {}.
-
unit : str =
- otto.monitor.parsers.register_host_parsers(host_id: str | Pattern[str], parsers: dict[str, MetricParser]) None¶
Associate a custom parser dict with a host ID or a host-ID pattern.
A plain string is an exact host ID (the key in
lab.hosts) — this is a total replacement for that host and may be re-registered freely. A compiledre.Patternscopes the dict to every host whose idfullmatch``es — one registration covers a family of hosts (e.g. ``re.compile(r"busybox-.*")). Precedence: exact id > pattern > project-level > defaults; two patterns matching the same host raise at resolution time. Call from an init module listed in.otto/settings.toml.Hosts with no registered parsers automatically fall back to DEFAULT_PARSERS.
- otto.monitor.parsers.register_parsers(parsers: Sequence[MetricParser]) None¶
Register project-level parsers that apply to every monitored host.
Call from an init module (listed in
.otto/settings.toml). Each parser’scommandbecomes its key: a command matching a DEFAULT_PARSERS entry overrides that built-in; a new command extends the set. Per-host registrations (register_host_parsers) take total precedence for their host. Registering the same command twice raises.
- otto.monitor.parsers.default_catalog() dict[str, MetricParser]¶
Return the parser catalog used when no per-host registration applies.
DEFAULT_PARSERS extended/overridden by project-level registrations.
- otto.monitor.parsers.get_host_parsers(host_id: str) dict[str, MetricParser]¶
Return the parser dict for host_id: exact > pattern > project-level > defaults.
An exact registration wins outright for its host_id — it is a total replacement, not merged with anything else, and shadows any pattern. Otherwise a single pattern registration whose
fullmatchmatches wins the same way; two or more matching patterns raise (no import-order-dependent silent winner). With neither, project-level parsers (seeregister_parsers()) are merged over DEFAULT_PARSERS. Non-raising for unregistered hosts by design: no registration at all is normal.