Files
james.maes 6ea2852af1 feat(devoverlay,logging): live log stream — every swift-log line into the overlay
Slice 3 of dev-overlay-end-to-end. Adds a fourth DevOverlay tab that
tails every log record from every Jarvis subsystem in real time —
agent, voice, vision, memory, MCP, bus, replay, system — without
polling files or relying on Console.app.

How it works:

1. `LogBroadcaster` (new) — process-wide actor with a 500-line ring
   buffer + subscriber set. Borrowed shape from
   OrchestratorEventBroadcaster so the project's three broadcasters
   share a mental model. New subscribers replay the buffer in order,
   then go live.

2. `BroadcastLogHandler` (new) — swift-log LogHandler conformance.
   Formats each log call into a `LogLine` and schedules an unstructured
   `Task { await LogBroadcaster.shared.publish(...) }` so the logging
   caller never blocks on actor isolation. Synchronous handler contract
   preserved; ordering is best-effort under heavy load.

3. `LoggingBootstrap.make(label:)` now returns
   `MultiplexLogHandler([file, os, broadcast])` — the new handler is
   purely additive. File rotation + os.Logger streaming unchanged.

4. `DevOverlayLogsView` (new) — fourth tab in the overlay. Subscribes
   to `LogBroadcaster.shared` on appear, caps at 1000 lines, renders
   monospaced. Per-channel filter (Menu w/ checkboxes), per-level
   filter (debug → critical), substring search, Pause/Resume button
   (Cmd+P), auto-scroll-to-bottom while not paused. Color-coded by
   level (warn=orange, error/critical=red).

Tests:
- 5 new LogBroadcasterTests covering publish/cap/replay/live/handler.
- swift test --package-path packages/Logging → 22/22 pass.
- swift test --package-path packages/DevOverlay → 14/14 pass.
- bash scripts/check-app-builds.sh → PASS.
- 18/18 boundary gates green.

Redaction note: file handler still routes through Redact.apply for
disk writes; the broadcaster sees raw messages because the DevOverlay
is a developer-only in-process surface. If this ever becomes
user-facing, route through Redact before publishing.
2026-05-12 14:27:57 -05:00

40 lines
1.6 KiB
Swift

// LoggingBootstrap.swift
//
// Single entry point for `swift-log` `LoggingSystem.bootstrap`. Builds
// the three-way MultiplexLogHandler that every Jarvis logger funnels
// through:
//
// 1. FileLogHandler — rotating disk persistence with redaction.
// 2. OSLogHandler — Console.app + system log streaming.
// 3. BroadcastLogHandler — in-process fan-out to DevOverlay et al.
//
// The third handler was added 2026-05-12 (dev-overlay-end-to-end Slice 3)
// so the DevOverlay can tail every log line from every module live. It is
// additive — removing it leaves persistence + os logging intact.
import Foundation
import Logging // swift-log
public enum JarvisLogHandlerFactory {
/// The subsystem string baked into OSLogHandler — matches CLAUDE.md §os.Logger identity.
public static let subsystem = "com.koftwentytwo.jarvis"
/// Factory consumed by swift-log's LoggingSystem.bootstrap.
public static func make(label: String) -> LogHandler {
let file = FileLogHandler(
label: label,
directory: LogPaths.channelDirectory,
dateProvider: SystemDateProvider()
)
let os = OSLogHandler(subsystem: subsystem, category: label, label: label)
let broadcast = BroadcastLogHandler(label: label)
return MultiplexLogHandler([file, os, broadcast])
}
/// Call exactly ONCE at `AppDelegate.applicationWillFinishLaunching`.
/// Per S-8: anti-pattern to call from package init or test setUp.
public static func bootstrap() {
LoggingSystem.bootstrap(Self.make)
}
}