Files
busymirror/BusyMirror/AppLogStore.swift
T
tomas.kracmar ad6ae396da Release 1.5.1
Bug fixes and code quality improvements:

- Fix mirror index dirtied on every sync (MirrorRecord.updatedAt in equality)
- Fix mirror URL corruption: encode calendar/source IDs before joining with ';'
  and use percentEncodedPath to prevent double-encoding
- Fix cleanup route mutating UI calendar picker selection unnecessarily
- Fix --exit flag redundancy (isCLIRun no longer implies termination)
- Remove dead SKIP_ALL_DAY_DEFAULT constant
- Replace deprecated FileHandle(forWritingAtPath:) with throwing variant
- Add EKEventStoreChanged observer for live calendar list refresh
- Extract AppLogStore into its own file (AppLogStore.swift)
- Add Block.span(start🔚) factory; replace verbose nil-field constructions
- Remove redundant MainActor.run{} wrappers inside @MainActor MirrorEngine
- Fix SettingsPayload indentation inside ContentView

All 45 unit tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 15:48:08 +02:00

52 lines
2.5 KiB
Swift

import Foundation
enum AppLogStore {
private static let queue = DispatchQueue(label: "BusyMirror.log.store")
private static let maxLogSizeBytes: UInt64 = 1_000_000
static let logDirectoryURL: URL = {
let base = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).first
?? FileManager.default.homeDirectoryForCurrentUser.appendingPathComponent("Library", isDirectory: true)
return base.appendingPathComponent("Logs/BusyMirror", isDirectory: true)
}()
static let logFileURL = logDirectoryURL.appendingPathComponent("BusyMirror.log", isDirectory: false)
private static let archivedLogFileURL = logDirectoryURL.appendingPathComponent("BusyMirror.previous.log", isDirectory: false)
static let launchdStdoutURL = logDirectoryURL.appendingPathComponent("launchd.stdout.log", isDirectory: false)
static let launchdStderrURL = logDirectoryURL.appendingPathComponent("launchd.stderr.log", isDirectory: false)
private static let timestampFormatter: ISO8601DateFormatter = {
let f = ISO8601DateFormatter()
f.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
return f
}()
static func append(_ message: String) {
let line = "[\(timestampFormatter.string(from: Date()))] \(message)\n"
queue.async {
let fm = FileManager.default
do {
try fm.createDirectory(at: logDirectoryURL, withIntermediateDirectories: true)
if let attrs = try? fm.attributesOfItem(atPath: logFileURL.path),
let size = attrs[.size] as? NSNumber,
size.uint64Value >= maxLogSizeBytes {
try? fm.removeItem(at: archivedLogFileURL)
try? fm.moveItem(at: logFileURL, to: archivedLogFileURL)
}
if !fm.fileExists(atPath: logFileURL.path) {
fm.createFile(atPath: logFileURL.path, contents: nil)
}
guard let data = line.data(using: .utf8) else { return }
// Use the throwing initialiser so we don't silently swallow
// an inaccessible file the outer catch handles it.
let handle = try FileHandle(forWritingTo: logFileURL)
defer { try? handle.close() }
try handle.seekToEnd()
try handle.write(contentsOf: data)
} catch {
// Logging must never break the app's main behavior.
}
}
}
}