logs
View stored output from managed services.
Info: From the interactive sysg status table, select a UNIT with
Tab/arrow navigation and press L to jump straight into that unit’s logs.
The status shortcut runs sysg logs -s <UNIT> -l 100 --stream 2, which shows
the latest 100 lines and refreshes the snapshot every 2 seconds.
Options
Scoping
Logs are always scoped — a bare sysg logs is refused
(SG0019) so you never get the wrong
project’s output. Each project’s logs live in their own place
({project}/{service}), so two projects can share a service name without their
logs colliding.
A bare -s <service> reads only the loose bundle. If the service is in a
project, pass -p; naming a non-loose service without -p is refused with
SG0021. Combining --supervisor with a
selector is SG0020.
Modes
logs is really five modes: show (the default), follow (--follow), print-path
(--path), purge (--purge), and prune (--prune). The mode flags are mutually
exclusive, and none combines with --follow — an illegal combination is refused
with SG0204 rather than guessed at.
--purge clears logs through the running supervisor when one is up, so both
the on-disk files and the supervisor’s in-memory buffer that sysg logs replays
from are dropped together. Clearing files alone would leave sysg logs still
showing the “purged” lines.
--prune needs a bound to prune against. Running it with neither --max-size
nor --max-age is refused with SG0017.
Examples
View one project’s service logs
View logs from specific service
To disambiguate a service in a multi-project supervisor, provide the stable
project id:
Info: By default, sysg logs shows a stacked stdout+stderr stream in
capture order. systemg adds its own UTC capture timestamp and stream label to
service output as it is written, then reads the combined service log for the
default view. Use --kind stdout or --kind stderr when you need a single
stream only.
Follow mode and non-interactive callers
Whether sysg logs follows the stream (stays attached and prints new lines as
they arrive) or prints a one-shot snapshot and exits depends on how it is
invoked:
--follow / -f always follows, until you interrupt it. In a terminal,
press Esc or Ctrl-C to stop following and return to the shell; the
stream reconnects on its own if the supervisor connection drops, so it never
freezes silently while a service is still logging.
--no-follow always prints a one-shot snapshot and exits.
- With neither flag, systemg follows only when stdout is an interactive
terminal and
SYSTEMG_AGENT is unset. When stdout is a pipe, a file, an SSH
command, or SYSTEMG_AGENT is set, it prints a one-shot snapshot and exits.
This keeps automation from wedging: a plain sysg logs -s api over
ssh host 'sysg logs -s api', sysg logs -s api | grep error, or any agent
returns immediately instead of blocking on a follow that can never be
interrupted.
--lines 0 --follow is the standard live-streaming form: it skips existing
log history, stays attached, and prints only newly emitted lines.
Purge logs for a specific service
This truncates api’s current stdout and stderr log files in place and does not print logs.
Purge logs for all services
This truncates all service log files, plus supervisor.log, in place and does not print logs.
View stderr logs
View supervisor logs
Show more history
Filter by pattern
--grep takes a regular expression and keeps only matching lines. It composes
with --kind, the time bounds, and --all.
Filter by time window
Time bounds are matched against systemg’s capture timestamp (the UTC prefix on
each stored line). --since / --until accept an RFC3339 timestamp, a bare UTC
date (taken as midnight), or a relative age in the past (30m, 2h, 7d).
Because following cannot honor an upper bound, any --since / --until /
--all read is a one-shot snapshot.
Read the full rotated history
By default sysg logs reads the latest 100 lines from the active
{service}.log; pass --lines N to choose another tail size. --all
concatenates the rotated backups
({service}.log.2, {service}.log.1) followed by the active file, oldest to
newest, so you can query the entire retained history without joining files by
hand. --all ignores --lines.
Machine-readable output
--format json emits one JSON object per captured line —
{"ts","stream","service","line"} — dropping banners and section headers so the
stream is a clean JSON-lines document. ANSI escapes are stripped automatically.
Project-wide filtering and aggregation
Every content flag — --grep, --since / --until, and --all — applies to
project-wide reads (--project, or the no-service default) exactly as it does to
a single --service. Each JSON record carries the resolved service name of the
unit that produced the line, so you can aggregate by service without querying each
one separately.
Raw application output
--raw prints only the application’s original log line, without systemg’s
capture timestamp and stream label — the closest equivalent to reading the
service’s own stdout. Banners are dropped and ANSI is stripped by default.
ANSI handling
systemg records service output verbatim, so lines a service colored for its own
terminal are stored with their ANSI escapes.
Info: When output is a pipe, a file, or --format json / --raw,
sysg logs strips those escapes so downstream tools (grep, jq, awk) see
clean text. Pass --no-strip-ansi to keep the color, or --strip-ansi to
force stripping even on an interactive terminal.
Stream snapshots
In stream mode, each refresh replaces the current frame with a new snapshot of
the latest --lines entries. Without --lines, each frame contains the latest
100 lines.
Prune rotated backups
Deletes rotated backup files ({name}.log.1, {name}.log.2, …) older than
--max-age and then trims the remaining backups oldest-first until their total
size is under --max-size. Active .log files are never touched. At least one
of --max-size or --max-age is required.
Log files
Logs are stored in ~/.local/share/systemg/logs/:
{service}.log - Canonical stdout+stderr stream in capture order
{service}_stdout.log / {service}_stderr.log - Legacy split-stream files from older systemg versions, still read as a fallback when no canonical log exists
supervisor.log - systemg’s own log output
Locate log files
Use --path to print where logs live instead of reading them — handy when you
need to point another tool at the files directly:
Rotation and retention
Every active log file (service logs and supervisor.log) is size-capped. When
an active file would exceed logs.max_bytes (default 10 MiB) it is rotated
to {name}.log.1, older backups shift up, and at most logs.max_files
(default 5) numbered backups are retained. A single log event larger than
16 KiB is truncated with a …[truncated N bytes] marker before it is
written, so one oversized payload cannot blow past the size cap. Tune the caps
with the logs.max_bytes / logs.max_files config keys, globally or per
service.
systemg’s own verbosity is info by default and honors per-crate RUST_LOG
directives (for example RUST_LOG=mycrate=debug,noisycrate=warn), so enabling
debug for one target does not pull in dependency-wide debug noise.
Each captured service line is stored with a systemg capture timestamp and stream
label:
Info: The leading timestamp is systemg’s own capture time; a service that
prints its own timestamp will show both. Use --raw to drop systemg’s prefix
and keep only the application’s line, or --format json to get the timestamp,
stream, and message as separate fields.
For the default file logging sink, systemg captures each service’s stdout and stderr through pipes and writes complete, immediately flushed lines through one per-service writer into {project}/{service}.log. A single-service follow (--follow, or the interactive default) tails that canonical file. A project-wide follow atomically takes a recent complete-line backlog and subscribes to future lines from the supervisor so several services can share one stream. The one-shot view (--no-follow, or the default when stdout is not an interactive terminal) reads the persisted active file once and exits. --kind stdout and --kind stderr filter captured lines by the stored stream label. The command itself is lightweight; the continuous cost comes from the capture threads, one writer thread, and one append-only file write path per service.
For high-output production workloads, configure logs.sink: none globally or per service to discard service output and avoid systemg log-writer threads, pipe backpressure from log capture, and file growth.
See also