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.
40 lines
1.6 KiB
Swift
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)
|
|
}
|
|
}
|