Peter Steinbergerbuild: add swift-logcb726da10 months agoHistory
123 lines · 4.0 KB · Swift
RawDownload
import Foundationimport Logging public enum CodexBarLog {    public enum Destination: Sendable {        case stderr        case oslog(subsystem: String)    }     public enum Level: String, Sendable {        case trace        case verbose        case debug        case info        case warning        case error        case critical         public var asSwiftLogLevel: Logger.Level {            switch self {            case .trace: .trace            case .verbose: .debug            case .debug: .debug            case .info: .info            case .warning: .warning            case .error: .error            case .critical: .critical            }        }    }     public struct Configuration: Sendable {        public let destination: Destination        public let level: Level        public let json: Bool         public init(destination: Destination, level: Level, json: Bool) {            self.destination = destination            self.level = level            self.json = json        }    }     private static let lock = NSLock()    private nonisolated(unsafe) static var isBootstrapped = false     public static func bootstrapIfNeeded(_ config: Configuration) {        self.lock.lock()        defer { lock.unlock() }        guard !self.isBootstrapped else { return }         let baseFactory: @Sendable (String) -> any LogHandler = { label in            switch config.destination {            case .stderr:                if config.json { return JSONStderrLogHandler(label: label) }                return StreamLogHandler.standardError(label: label)            case let .oslog(subsystem):                #if canImport(os)                return OSLogLogHandler(label: label, subsystem: subsystem)                #else                if config.json { return JSONStderrLogHandler(label: label) }                return StreamLogHandler.standardError(label: label)                #endif            }        }         LoggingSystem.bootstrap { label in            var handler = baseFactory(label)            handler.logLevel = config.level.asSwiftLogLevel            return handler        }         self.isBootstrapped = true    }     public static func logger(_ category: String) -> CodexBarLogger {        let logger = Logger(label: "com.steipete.codexbar.\(category)")        return CodexBarLogger { level, message, metadata in            let swiftLogLevel = level.asSwiftLogLevel            let meta = metadata?.reduce(into: Logger.Metadata()) { partial, entry in                partial[entry.key] = .string(entry.value)            }            logger.log(level: swiftLogLevel, "\(message)", metadata: meta)        }    }     public static func parseLevel(_ raw: String?) -> Level? {        guard let raw, !raw.isEmpty else { return nil }        return Level(rawValue: raw.trimmingCharacters(in: .whitespacesAndNewlines).lowercased())    }} public struct CodexBarLogger: Sendable {    private let logFn: @Sendable (CodexBarLog.Level, String, [String: String]?) -> Void     fileprivate init(_ logFn: @escaping @Sendable (CodexBarLog.Level, String, [String: String]?) -> Void) {        self.logFn = logFn    }     public func trace(_ message: @autoclosure () -> String, metadata: [String: String]? = nil) {        self.logFn(.trace, message(), metadata)    }     public func debug(_ message: @autoclosure () -> String, metadata: [String: String]? = nil) {        self.logFn(.debug, message(), metadata)    }     public func info(_ message: @autoclosure () -> String, metadata: [String: String]? = nil) {        self.logFn(.info, message(), metadata)    }     public func warning(_ message: @autoclosure () -> String, metadata: [String: String]? = nil) {        self.logFn(.warning, message(), metadata)    }     public func error(_ message: @autoclosure () -> String, metadata: [String: String]? = nil) {        self.logFn(.error, message(), metadata)    }     public func critical(_ message: @autoclosure () -> String, metadata: [String: String]? = nil) {        self.logFn(.critical, message(), metadata)    }}