systemg

Search docs

/
Install

Commands

logs

View stored output from managed services.

$ sysg logs -p myapp

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

ShortLongDescription
-c--configPath to configuration file
---purgeReset log files instead of displaying them
---pruneDelete rotated log backups instead of displaying logs (requires --max-size and/or --max-age)
---max-sizeWhen pruning, cap total rotated-backup size (e.g. 500MB, 2g)
---max-ageWhen pruning, remove rotated backups older than this (e.g. 7d, 12h)
-s--serviceThe service whose logs to show. With no -p, resolves to the loose (__loose__) bundle only
-p--projectThe project whose logs to show (all its services, or one with -s)
---supervisorShow the supervisor's own log instead of a service's. Cannot be combined with -s/-p
-l--linesTrailing lines to show. Defaults to the latest 100; pass -l N to choose another limit
-k--kindKind of logs to show: stdout or stderr. Omit to show stdout+stderr together
-f--followFollow the log stream until interrupted (like tail -F)
---no-followForce a one-shot snapshot even on an interactive terminal
---sinceOnly show lines captured at or after this time (RFC3339, YYYY-MM-DD, or a relative age like 30m/2h/7d)
---untilOnly show lines captured at or before this time (same formats as --since)
-g--grepOnly show lines matching this regular expression
-a--allRead the full active-plus-rotated history instead of the last --lines
---pathPrint the on-disk log path(s) instead of the logs, then exit
---formatMachine-readable output. json prints one {ts, stream, service, line} object per line
---rawPrint only the application's original line, dropping systemg's timestamp/stream prefix
---strip-ansiStrip ANSI color/escape sequences (default on for --format json, --raw, and non-interactive output)
---no-strip-ansiKeep ANSI escape sequences even when they would be stripped by default
---streamContinuously refresh the latest log snapshot at the provided interval (e.g., 5, 1s, 2m)
-v--verbosePrint operation progress
---sysOpt into privileged system mode. Requires running as root
---drop-privilegesAccepted globally but ignored; logs does not spawn services
---plainDisable color, banners, paging, and implicit log following
---log-levelSet logging verbosity for this invocation (trace through off, or 5-0)

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.

You runYou get
logs -p <project>all of that project's services
logs -p <project> -s <service>that one service
logs -s <service> (no -p)the loose (__loose__) service of that name only
logs --supervisorthe supervisor's own log
logs (nothing)refused — SG0019

Note

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.

Note

--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.

Warning

--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

$ sysg logs --project myapp

View logs from specific service

$ sysg logs --service api

To disambiguate a service in a multi-project supervisor, provide the stable project id:

$ sysg logs --project arbitration --service api

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.

$ sysg logs -p arbitration-dev -s arb_rs__dev --lines 0 --follow
$ sysg logs --service api --no-follow    # one snapshot, then exit
$ sysg logs --service api | grep ERROR   # auto one-shot: stdout is a pipe

--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

$ sysg logs --service api --purge

This truncates api's current stdout and stderr log files in place and does not print logs.

Purge logs for all services

$ sysg logs --purge

This truncates all service log files, plus supervisor.log, in place and does not print logs.

View stderr logs

$ sysg logs --service api --kind stderr

View supervisor logs

$ sysg logs --supervisor

Show more history

$ sysg logs --service api --lines 200

Filter by pattern

$ sysg logs --service api --grep ERROR

--grep takes a regular expression and keeps only matching lines. It composes with --kind, the time bounds, and --all.

Filter by time window

$ sysg logs --service api --since 2h
$ sysg logs --service api --since 2026-07-07 --until 2026-07-07T12:00:00Z

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

$ sysg logs --service api --all --grep ERROR

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

$ sysg logs --service api --format json

--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

$ sysg logs --project arbitration --all --since 2026-07-08 --grep openai_ --format json --no-follow

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.

$ sysg logs --service api --format json --grep ERROR | jq -r .line

Raw application output

$ sysg logs --service api --raw

--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

$ sysg logs --service api --lines 200 --stream 2

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

$ sysg logs --prune --max-size 500MB --max-age 7d

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:

$ sysg logs --path                       # the log directory
$ sysg logs --service api --path          # that service's active log file
$ sysg logs --service api --path --all    # active file plus rotated backups

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:

2026-05-14T02:05:13.470911Z stderr Compiling tungstenite v0.28.0
2026-05-14T02:05:13.471020Z stdout request completed status=200

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.

Performance model

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

restartstatus