Logging¶
Everything an otto invocation emits flows through one model: three sinks
fed by one queue, with a single per-host/per-command knob
(LogMode) deciding what command I/O shows up
where. What a verb returns is the other cross-cutting spine — see
Results and exit codes.
Three sinks¶
CLI logging writes to three places, wired per invocation by
otto.logger.management into the per-command output directory:
Sink |
Level |
Purpose |
|---|---|---|
console (Rich) |
|
what the operator watches; timestamps only with |
|
|
a faithful transcript of the console — same records, always timestamped |
|
INFO floor, DEBUG when |
the everything-record, including what the console suppressed |
Handlers hang off a QueueListener, so slow file I/O (e.g. logs on NFS)
never blocks the event loop, and old run directories are pruned under a
time-boxed budget so rotation cannot stall startup on slow mounts.
LogMode: one knob for command I/O¶
Whether a host’s command echo and output show up is a per-host and
per-command disposition, LogMode:
NORMAL— logged at the call’s native level, visible everywhere.QUIET— suppressed from the console andconsole.log, kept inverbose.log. For routine chatter: file-op read bodies,lsmodscrapes, config probes.NEVER— redacted from every sink. For secrets (ansupassword) and bulk noise (a hex firmware payload streamed over a console).
The effective mode composes most-restrictive-wins
(effective_mode()): a QUIET host running a NEVER
command yields NEVER. If either party considers the I/O sensitive, the
stricter disposition holds.
Scope is the important invariant: LogMode gates command I/O only —
records tagged with the host that emitted them. Framework diagnostics,
warnings, and errors are never suppressed by LogMode; a NEVER host still
logs its connection failures. This is why the monitor can set its polling
hosts to NEVER (otto monitor — the observation pipeline) without hiding real
problems.
otto as a library citizen¶
otto.logger attaches only a NullHandler to the 'otto' logger —
importing otto never configures logging; the handler topology above is
strictly CLI-side (otto.logger.management never imports otto.context, and
nothing in the library configures handlers). The reverse direction is also
covered: capture_external_loggers routes named third-party logger trees
(product code using logging.getLogger(__name__)) into otto’s sinks, so
suite and instruction logs land in the same transcript as otto’s own.