Originally created by: kumaakh
apra-fleet currently uses console.error for all server-side logging. The MCP spec explicitly permits stderr for logging in stdio transport, but Claude Code CLI does not persist MCP server stderr to a file (see anthropics/claude-code#29035 — Claude Desktop does, Claude Code CLI does not). As a result, fleet's internal logs (tool entry/exit, PID capture, SSH events, errors) are invisible during active tool execution — stderr is only captured at server startup.
Additionally, T9 (Sprint 3) added logLine() to new call sites, but hundreds of pre-existing direct console.error / console.warn / console.log calls across providers, auth, and error paths still bypass logLine() entirely.
Replace the console.error backend in logLine() with pino writing JSONL to APRA_FLEET_DATA_DIR/logs/fleet-<pid>.log.
APRA_FLEET_DATA_DIR/logs/fleet-<pid>.log — one file per fleet server process, named by OS PID. No cross-process file contention, no locking, works on Windows and POSIX. Multiple fleet instances (one per Claude/Gemini window) each own their file.| Field | Type | Notes |
|---|---|---|
ts |
ISO 8601 | Timestamp |
pid |
number | Fleet server OS PID (matches filename — self-identifying if logs are aggregated) |
level |
string | info / warn / error |
tag |
string | Tool or subsystem: execute_prompt, execute_command, ssh, vcs-auth, etc. |
member_id |
string? | Optional member GUID when the log is specific to a member — enables per-member log filtering in the dashboard (#188) |
msg |
string | Human-readable message (secrets masked via maskSecrets()) |
pino dependency — async worker-thread transport (genuinely non-blocking, never stalls a tool handler), zero-allocation hot path, TypeScript types included.logLine() — create a pino instance at module load, write JSONL to fleet-<pid>.log, keep console.error alongside for stderr capture.logLine() signature — add optional memberId?: string parameter; include in every log line emitted by member-specific tool handlers.console.* call sites — inventory every console.error, console.warn, console.log across the fleet server codebase and replace with logLine(). T9 only covered new call sites.pino-roll transport or a simple size-cap (10 MB → rename to .1.log).The member_id field allows the dashboard to filter and display logs per member. JSONL format enables reuse of standard open-source log parsing libraries without a custom parser.
logLine(tag, msg) public signature is unchanged. All call sites need only add memberId where available — the rest is an internal backend swap.
Ticket changed by: kumaakh