Skip to main content
Comis writes detailed logs about everything it does — they are your primary tool for understanding what is happening and diagnosing problems. Every message processed, every agent execution, every health check, and every error is recorded.

Where Logs Go

Comis writes logs to two places simultaneously:
  • Log file: ~/.comis/logs/daemon.log — A structured JSON file that is automatically rotated when it gets too large (10 MB by default). The daemon keeps up to 5 rotated copies before deleting the oldest.
  • Console output: The same log data is also written to standard output. This is what you see when using pm2 logs or journalctl.
Logs are in JSON format for machine readability. Each log line is a JSON object with fields like level, msg, module, and timestamps. You typically view them through pm2 or journalctl, which display them line by line.

Log Levels

Comis uses Pino’s standard log levels plus a custom audit level. From most severe to least:
The silent value exists at the logger API layer to suppress output, but the daemon.logLevels configuration only accepts trace, debug, info, warn, error, and fatal. To quiet a noisy module, raise its level (for example, set it to error) rather than relying on silent.
Start with INFO (the default). Only switch to DEBUG when troubleshooting a specific problem — DEBUG produces significantly more output. Use TRACE only for deep protocol debugging. The FATAL and AUDIT levels are used by the system automatically — you typically do not set them as module log levels.

Viewing Logs

View the last 50 lines (snapshot):
Follow logs in real time (press Ctrl+C to stop):
pm2 keeps its own log files at ~/.pm2/logs/. The --lines flag shows recent lines from those files, and omitting --nostream follows them live.

Changing Log Levels

Per-Module Overrides

Comis is organized into modules (daemon, agent, channels, scheduler, etc.), and you can set a different log level for each one. This is useful when you want to debug a specific part of the system without being overwhelmed by output from everything else. Add a logLevels section under daemon in your configuration:
Each module only logs messages at or above its configured level. In the example above, the agent module logs everything including DEBUG messages, while the channels module only logs warnings and errors.
The most common use case is setting agent: "debug" to see exactly what your agent is doing during a conversation — which tools it calls, how long each LLM request takes, and what context it retrieves from memory.

Runtime Level Changes

You can change log levels without restarting the daemon by using the daemon.setLogLevel RPC command. This is useful for temporarily enabling DEBUG logging to investigate an issue, then switching back to INFO when you are done.
Runtime level changes take effect immediately and persist until the daemon restarts, at which point the levels revert to whatever is in your configuration file.

Error Classification

Every ERROR and WARN log entry includes two mandatory fields that help you quickly understand and act on problems:
  • hint — A human-readable string explaining what to do about the error
  • errorKind — A classification tag from a fixed set of 9 categories

ErrorKind Values

Filter logs by errorKind to quickly find related problems. For example, to see all authentication errors: grep '"errorKind":"auth"' ~/.comis/logs/daemon.log

Memory and Recall Signals

The memory module (set its level under daemon.logLevels.memory, shown above) emits two families of structured signal: step-tagged write/curation logs and graceful-degradation WARNs.

Step-tagged write and curation logs

Memory writes and the background curation jobs carry a step tag plus a durationMs timing field, so you can filter the memory pipeline by stage:
The aggregate outcome of these passes is also emitted as the typed memory:entities_linked and memory:consolidated events — see Observability → Memory & Recall Diagnostics.

Graceful-degradation WARNs (no silent degradation)

Recall degrades gracefully, and every degradation path logs a WARN carrying the mandatory errorKind + hint pair (see Error Classification above) — recall never drops to a cheaper path silently: As with all Comis logs, these never log secrets, message bodies, or absolute paths — the per-recall ranking detail lives only in the opt-in, full-sanitized recall-trace artifact, never in daemon.log.
For the full “why did recall pick X / why is recall slow?” diagnosis flow, see the Troubleshooting runbook.

Field Dictionary

Every log entry is a JSON object. Beyond the standard Pino fields (level, time, msg, pid, hostname), Comis uses 48 canonical fields organized by category. These are the fields you will see when reading JSON log output.

Core Identity

Timing

Operation

Error

Token and Cost

Pipeline

WebSocket

Message

Agent Execution

System

Truncation Flags

Security

Comis automatically redacts sensitive information from log output. API keys, tokens, passwords, and other credentials are replaced with [REDACTED] before being written to the log file or console. The redaction engine covers all credential field name patterns across multiple nesting depths (top-level through 3 levels of nesting): Nested paths are also redacted: config.telegram.botToken, *.*.apiKey, etc., up to 4 levels deep (*.*.*.*).
You do not need to worry about sensitive data in logs. Comis automatically detects and hides API keys, tokens, and passwords using a compiled redaction engine. The redaction rules are compiled once at startup for zero runtime overhead.

Log Rotation

Comis automatically rotates all observability log streams to bound disk usage.

observability.logRotation — Cross-Stream Policy

A single policy governs five log streams under ~/.comis/logs/:
  • daemon.log — daemon-wide structured JSON log
  • cache-trace.jsonl — per-turn cache observability (cache hits, misses, tokens)
  • config-audit.jsonl — config-write audit trail (all writes to config.yaml)
  • session-index.YYYY-MM-DD.jsonl — append-only session index (date-rolled, one file per UTC day)
  • *.trajectory.jsonl — per-session trajectory recordings (one file per agent session)
Configure the policy under observability.logRotation in your config.yaml:

How to Verify

Inspect the resolved policy, including any operator overrides, with:

Storage Budget

Worst-case storage: 5 streams × 5 files × 50 MB = 1.25 GB. With compressAged: true (the default), gzipped JSON text compresses approximately 5×, so realistic steady-state is ~300 MB. Increase maxFiles or maxAgeDays if you need more history; reduce them on tight disks.

Per-Stream Behavior

daemon.log — pino-roll handles size-based rotation in a worker thread; rotated files become daemon.1.log, daemon.2.log, etc. The daemon-startup sweep gzips any uncompressed rotated files and removes those beyond maxFiles or older than maxAgeDays. cache-trace.jsonl — a per-file cap (50 MB) prevents a single cache-trace file from growing unbounded. The startup sweep prunes accumulated history against maxFiles and maxAgeDays. config-audit.jsonl — a small (10 MB) rename-shift rotation runs on every append, keeping the active file write-friendly. The startup sweep gzips the resulting rotated copies and prunes them by count and age. session-index.YYYY-MM-DD.jsonl — a new file starts each UTC day (date-roll). Old dated files are pruned by the startup sweep against maxFiles and maxAgeDays. Only today’s dated file is treated as the active base and is never pruned. *.trajectory.jsonl — one file per agent session, with a 10 MB soft cap and a 50 MB hard cap applied in-session. Accumulated session files across past sessions are pruned by the startup sweep against maxFiles and maxAgeDays.

Per-File daemon.logging Keys

The daemon.logging.maxSize and daemon.logging.maxFiles keys (documented below) apply to daemon.log specifically. When both key families are set, observability.logRotation takes precedence for the cross-stream sweep, and daemon.logging.maxSize retains its role as the pino-roll size threshold when observability.logRotation.maxSizeBytes is unset.
Prefer observability.logRotation for a unified, consistent rotation policy across all five log streams. The daemon.logging keys govern only daemon.log.

daemon.logging Configuration

You can customize per-file daemon-log rotation with these keys:
With the cross-stream policy active (the default), observability.logRotation controls the 50 MB roll threshold and the 5-file / 30-day retention for all streams. The daemon.logging.maxSize value still reaches pino-roll as the size threshold if you have not set observability.logRotation.maxSizeBytes.

Trace Files

In addition to the main log file, Comis can write per-agent JSONL trace files that capture detailed execution data. These are useful for debugging specific agent conversations.
Trace files are created automatically when an agent processes messages. They contain the full execution timeline: prompts sent, responses received, tools called, and timing data.

Daemon

How the daemon starts, runs, and shuts down.

Monitoring

Health checks that watch your system’s vital signs.

Observability

Token tracking, cost estimation, and performance data.

Troubleshooting

Common problems and how to solve them.