Documentation

Logging

Stowage uses the standard library's log/slog and writes to stdout. There is no log file rotation built in — let the platform around Stowage (systemd journal, Docker logging driver, sidecar) handle that.

#Format

log:
  level: info
  format: json
FormatUse when
jsonProduction. Parsers like Loki / Vector / Splunk eat it directly.
textLocal development, where you're tailing on a terminal.

The default in config.Defaults() is json. config.demo.yaml uses text because it's friendlier when kicking the tyres.

#Levels

LevelWhat's emitted
debugPer-request handler decisions, retry decisions, source-of-truth merges. Verbose.
infoDefault. Startup banner, config summary, audit-recorder lifecycle, scheduled scans.
warnRecoverable problems: backend probe failure, audit-queue overflow, secret-key auto-regenerated on first boot.
errorUnrecoverable per-request errors. The handler still returns a response; this just records why.

#Useful structured fields

Every request log line includes:

  • req_id — chi's RequestID middleware (UUID).
  • remote_addr — after server.trusted_proxies resolution.
  • method, path, status, duration_ms, bytes_written.
  • user_id (if a session was attached).
  • backend (for routes scoped to a backend).

Audit events have their own table in SQLite (see Audit) — the structured logs are best-effort runtime breadcrumbs, not a forensic trail.

#Tuning verbosity at runtime

There is no signal-driven log-level toggle. Restart Stowage with STOWAGE_LOG_LEVEL=debug to bump verbosity briefly:

STOWAGE_LOG_LEVEL=debug ./stowage serve --config /etc/stowage/config.yaml

For production, prefer leaving the level at info and querying the audit log when you need to reconstruct user actions.

#What is NOT logged

  • Passwords (plaintext or hashed).
  • AES key material.
  • Session tokens or CSRF cookie values.
  • Share password attempts (the audit row records share.access with status error when the password is wrong; the password itself never hits stdout).

#See also