mirror of
https://github.com/dredx/prole.git
synced 2026-09-24 16:54:32 +00:00
- Moved prole-tools-app to prole-app at the project root to make it self-contained for transition to its own repository. - Created prole-tools-app/dist/ directory to host build artifacts. - Generated distribution artifacts (Prole Tools.app and Prole Tools.zip) using prole-app/build.sh package. - Checked in the generated artifacts to prole-tools-app/dist/ (bypassing .gitignore for temporary release process). Changes Summary • Renamed directory prole-tools-app/ to prole-app/. • Populated prole-tools-app/dist/ with the latest build output from prole-app/build.sh. • Staged all changes, including the forced addition of ignored artifacts in prole-tools-app/dist/.
78 lines
2.8 KiB
Swift
78 lines
2.8 KiB
Swift
import Foundation
|
|
|
|
final class Logger {
|
|
static let shared = Logger()
|
|
private let queue = DispatchQueue(label: "org.prole.tools.logger", qos: .utility)
|
|
private var currentDateStr: String = Logger.dateString(Date())
|
|
|
|
private init() {
|
|
// Ensure directories exist
|
|
ProleEnv.bootstrap()
|
|
// Ensure current symlink points to today's file (best effort)
|
|
queue.async { [weak self] in self?.ensureSymlink() }
|
|
}
|
|
|
|
private static func dateString(_ date: Date) -> String {
|
|
let fmt = DateFormatter()
|
|
fmt.locale = Locale(identifier: "en_US_POSIX")
|
|
fmt.dateFormat = "yyyy-MM-dd"
|
|
return fmt.string(from: date)
|
|
}
|
|
|
|
private func logDir() -> URL { ProleEnv.logsDir() }
|
|
|
|
private func datedLogURL(for dateStr: String) -> URL {
|
|
return logDir().appendingPathComponent("prole-tools-app-\(dateStr).log")
|
|
}
|
|
|
|
private func currentLogURL() -> URL {
|
|
return logDir().appendingPathComponent("prole-tools-app.log")
|
|
}
|
|
|
|
private func ensureSymlink() {
|
|
let fm = FileManager.default
|
|
let target = datedLogURL(for: currentDateStr)
|
|
let link = currentLogURL()
|
|
// If link exists and points to target, nothing to do
|
|
if let attrs = try? fm.attributesOfItem(atPath: link.path), attrs[.type] as? FileAttributeType == .typeSymbolicLink {
|
|
if let dest = try? fm.destinationOfSymbolicLink(atPath: link.path), dest.hasSuffix(target.lastPathComponent) {
|
|
return
|
|
}
|
|
}
|
|
// Recreate symlink to today's file
|
|
_ = try? fm.removeItem(at: link)
|
|
try? fm.createSymbolicLink(at: link, withDestinationURL: target)
|
|
}
|
|
|
|
private func rotateIfNeeded(now: Date = Date()) {
|
|
let today = Logger.dateString(now)
|
|
if today != currentDateStr {
|
|
currentDateStr = today
|
|
ensureSymlink()
|
|
}
|
|
}
|
|
|
|
func info(_ message: String) { write(level: "INFO", message) }
|
|
func error(_ message: String) { write(level: "ERROR", message) }
|
|
|
|
func write(level: String, _ message: String) {
|
|
queue.async {
|
|
self.rotateIfNeeded()
|
|
let ts = ISO8601DateFormatter().string(from: Date())
|
|
let line = "[\(level)] \(ts) \(message)\n"
|
|
let fileURL = self.datedLogURL(for: self.currentDateStr)
|
|
if let data = line.data(using: .utf8) {
|
|
if FileManager.default.fileExists(atPath: fileURL.path) {
|
|
if let h = try? FileHandle(forWritingTo: fileURL) {
|
|
defer { try? h.close() }
|
|
try? h.seekToEnd()
|
|
try? h.write(contentsOf: data)
|
|
}
|
|
} else {
|
|
try? data.write(to: fileURL, options: .atomic)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|