Logging System
Relevant source files
The following files were used as context for generating this wiki page:
The Crisp logging system is a cross-platform diagnostic architecture designed to provide a unified timeline of events across both the Swift-based macOS application and the Python-based cleaning engine. It utilizes a per-channel, per-day rotating file strategy where both layers append logs to the same physical file, ensuring that developers can trace a video cleaning operation from the UI interaction down to specific subprocess commands and ML model inferences.
The system is built for concurrency, using POSIX-compliant atomic appends to allow the main application, background watchers, and multiple parallel engine subprocesses to write to the same log file without line interleaving or data corruption. In addition to file-based persistence, the Swift layer "tees" logs to Apple's Unified Logging system for real-time monitoring via Console.app.
Sources: CLAUDE.md:129-141, apps/desktop/Sources/CrispCore/Common/FileLog.swift:68-81
Architecture and Data Flow
The logging architecture is divided between the Swift CrispCore framework and the Python crisp package. Both layers target the same log directory, which is determined by the application's release channel (Stable, Nightly, or Dev).
Unified Log Sinks
- File Log (
FileLog.shared): A serial-queue-backed writer that manages daily.logfiles in~/.crisp*/logs/. - Apple Unified Logging: The Swift layer uses
os.Loggerto send entries to the system-wide log buffer. - NDJSON Stream: The Python engine emits high-level events (progress, results, errors) via
stdoutin NDJSON format, which the SwiftCleanRunnerparses to update the UI.
The following diagram illustrates how different components interact with the shared file sink:
Swift Logging Implementation
The Swift implementation revolves around the CrispLog struct and the FileLog singleton. CrispLog provides a familiar API (debug, info, notice, error) that mirrors os.Logger and supports privacy-aware string interpolation.
Key Components
CrispLog: The primary interface for logging. It acceptsCrispLogMessagewhich allows for\(value, privacy: .public)syntax.FileLog: Manages the file descriptor and rotation. It usesO_APPEND | O_CREATwith0o600permissions to ensure logs are private to the user.StderrDrain: Used byCleanRunnerto capture thestderrof the Python subprocess, ensuring that unhandled Python tracebacks are preserved in the logs.
| Component | Responsibility | File Path |
|---|---|---|
FileLog | Atomic file writing and daily rotation | FileLog.swift |
CrispLog | Teeing logs to OS and File sinks | FileLog.swift |
CleanRunner | capturing engine stderr | CleanRunner.swift |
Sources: apps/desktop/Sources/CrispCore/Common/FileLog.swift:42-66, apps/desktop/Sources/CrispCore/Engine/CleanRunner.swift:233-241
Python Engine Logging
The Python engine uses crisp/enginelog.py (documented in CLAUDE.md) to write to the same directory passed via the CRISP_LOG_DIR environment variable.
Engine Log Features
- Merged Timeline: The line format (Timestamp - Level - Category - Message) matches the Swift format exactly.
- Command Logging: The engine logs every
ffmpegandwhisper.cppcommand, including exit codes andstderron failure. - Exception Handling: Unexpected exceptions are caught and turned into logged tracebacks before emitting an NDJSON error to the UI.
Log Management and Maintenance
To prevent infinite disk usage, the Swift application performs maintenance on the log directory.
- Rotation: Logs are partitioned by date (e.g.,
2026-06-18.log). Therotate(to:)function handles opening the new file and closing the old one. - Pruning: At launch,
CrispAppcallsFileLog.shared.pruneOldLogs(keepingDays: 30). This asynchronously scans the logs directory and removes files with acontentModificationDateolder than 30 days.
public func pruneOldLogs(keepingDays days: Int = 30) {
queue.async {
guard let items = try? self.fm.contentsOfDirectory(
at: self.directory, includingPropertiesForKeys: [.contentModificationDateKey]) else { return }
let cutoff = Date().addingTimeInterval(-Double(days) * 86_400)
for url in items where url.pathExtension == "log" {
let modified = (try? url.resourceValues(forKeys: [.contentModificationDateKey])
.contentModificationDate) ?? Date()
if modified < cutoff { try? self.fm.removeItem(at: url) }
}
}
}
Sources: apps/desktop/Sources/CrispCore/Common/FileLog.swift:107-120, CLAUDE.md:137-138
Summary
The Crisp logging system is a robust, cross-platform diagnostic tool that bridges the gap between a high-level SwiftUI application and a low-level Python processing engine. By centralizing logs into a single, daily-rotating, atomically-appended file, it provides a comprehensive audit trail for performance monitoring and error resolution while respecting user privacy and system resources through automatic pruning.