Logging and profiling

The LOG_LEVEL grammar and its four modules, timestamps, the stderr-only rule, NO_COLOR, and the three artifacts DEBUG_PERF captures.

Logging is configured by the environment, so it applies to every command and works before any flag is parsed. serve, pull, and commit also take --log-level and --log-timestamp, which override the environment once the flags resolve; the few records emitted before that point — the command router, a configuration refusal — follow the environment.

Variable Effect
LOG_LEVEL Per-module levels. A bare level is the default.
SLIVINGDOC_LOG_TIMESTAMP false removes the time= field, for hosts that stamp log lines themselves.
NO_COLOR Any non-empty value disables ANSI colour and the terminal marks and spinners.
DEBUG_PERF Captures CPU, heap, and execution-trace profiles across one whole command.

Records

Records are structured key=value text on stderr. Each one carries a timestamp (unless timestamps are off), a level, and the module that emitted it.

Warning: Stdout carries only MCP protocol messages and command output. Nothing ever logs to stdout — that is what keeps the stdio transport usable.

LOG_LEVEL

LOG_LEVEL takes a comma-separated list. module=level sets one module; a bare level sets the default for the rest:

LOG_LEVEL="cli=warn,mcp=debug,info"

The modules are:

Module Emits
cli Command routing.
app Startup and shutdown.
mcp A start and a completion record per tool call, both carrying mcpReqID.
notebook Best-effort checkpoint and cleanup records, and warnings when a pack cannot be written to the shared cache.

The levels are debug, info, warn (or warning), and error. The default is info.

A malformed LOG_LEVEL is reported and falls back to info; it never refuses startup. An invalid --log-level flag value, in contrast, refuses startup like any other flag. The flag takes the same grammar as the environment variable.

Startup records

At startup the app module writes, at info, which store the process chose. Neither record carries a key or a token.

storage selected backend=hosted endpoint=https://api.slivingdoc.dev space=notes token=login
hosted space resolved space=notes from=default space
  • storage selected names the backend (s3 or hosted), the endpoint, the space (the bucket in S3 mode), and where the credential came from: token is none for S3, env for SLIVINGDOC_TOKEN, or login for a stored login. An S3 process with no endpoint of its own logs aws-default (SDK: env or profile), or the AWS variable that supplies one. A hosted process with a token and no named space logs the token's own here, before the lookup.
  • hosted space resolved follows in hosted mode with the final space and where it came from: --space, --bucket, SLIVINGDOC_SPACE, SLIVINGDOC_BUCKET, default space (the login’s stored default), or token.

These two records are the quickest way to see why a process picked S3 or hosted storage; see Choosing the storage. At debug, the app module also says why the shared pack cache is not in use, when it is not.

Correlating a tool call

The mcp module writes one pair of records per tool call — a start record and a completion record — and both carry the same mcpReqID. That value is the diagnosticId an agent sees in a structured error, so a failing call in an agent transcript points straight at the two log records that describe it. The completion record carries duration and an outcome of ok, error, or invalid_request; an error outcome also carries cause. Both failing outcomes are logged at warn, the start and the successful completion at info. It is also how an operator reads the full cause of an ENGINE_FAILED error, which the envelope itself withholds. See Errors.

Timestamps

--log-timestamp (environment SLIVINGDOC_LOG_TIMESTAMP, default true) set to false removes the time= field. Use it when the host already stamps every line it captures, so the record is not stamped twice.

Colour

Any non-empty NO_COLOR disables the ANSI colour of the log levels, and makes every terminal presentation plain at the same time: the CLI report’s colour and marks, the home screen, help headers, the progress spinner, and the styled login output. Interactive pickers still appear on a terminal. The convention is the value being non-empty, not the literal string true. See CLI for what a terminal report adds.

Profiling with DEBUG_PERF

DEBUG_PERF captures performance profiles across one whole command — startup, the operation, and shutdown — for finding where a slow pull or commit spends its time.

Value Effect
1 or true Write under slivingdoc-perf/ in the system temporary directory.
0, false, empty No capture.
Any other value Use that value as the base directory.
DEBUG_PERF=1 slivingdoc pull ./notes

Each invocation creates its own timestamped run directory under the base, so repeated benchmark runs never overwrite each other and can be compared with go tool pprof -diff_base. A run directory holds three artifacts:

File Shows
cpu.pprof Where CPU time went: go tool pprof cpu.pprof.
heap.pprof What the command retained at exit: go tool pprof heap.pprof.
trace.out The execution timeline, including time blocked on the network and on locks: go tool trace trace.out.

For an operation dominated by object-store round trips, the CPU profile stays near-empty and trace.out shows the waiting. That split is the reason both are captured.

The exact paths are reported on stderr when the capture starts and finishes; stdout stays protocol-only. A capture that cannot start or finish is a warning, never a refusal, and it never changes the command’s result or exit code.

Next

  • Errors — the diagnostic ID and what each error means.
  • CLI — exit behaviour and the report grammar.
  • Configuration — --log-level and --log-timestamp beside every other flag.

Last updated September 29, 2026

Type to search the documentation.