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) }}
import Foundation
import 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)
}
}