Protocols
The following protocols are available globally.
-
A
LogHandler
is an implementation of a logging backend.This type is an implementation detail and should not normally be used, unless implementing your own logging backend. To use the SwiftLog API, please refer to the documentation of
Logger
.Implementation requirements
To implement your own
LogHandler
you should respect a few requirements that are necessary so applications work as expected regardless of the selectedLogHandler
implementation.- The
LogHandler
must be astruct
. - The metadata and
logLevel
properties must be implemented so that setting them on aLogger
does not affect otherLogger
s.
Treat log level & metadata as values
When developing your
LogHandler
, please make sure the following test works.LoggingSystem.bootstrap(MyLogHandler.init) // your LogHandler might have a different bootstrapping step var logger1 = Logger(label: "first logger") logger1.logLevel = .debug logger1[metadataKey: "only-on"] = "first" var logger2 = logger1 logger2.logLevel = .error // this must not override `logger1`'s log level logger2[metadataKey: "only-on"] = "second" // this must not override `logger1`'s metadata XCTAssertEqual(.debug, logger1.logLevel) XCTAssertEqual(.error, logger2.logLevel) XCTAssertEqual("first", logger1[metadataKey: "only-on"]) XCTAssertEqual("second", logger2[metadataKey: "only-on"])
Special cases
In certain special cases, the log level behaving like a value on
Logger
might not be what you want. For example, you might want to set the log level across allLogger
s to.debug
when say a signal (eg.SIGUSR1
) is received to be able to debug special failures in production. This special case is acceptable but we urge you to create a solution specific to yourLogHandler
implementation to achieve that. Please find an example implementation of this behavior below, on reception of the signal you would callLogHandlerWithGlobalLogLevelOverride.overrideGlobalLogLevel = .debug
, for example.import class Foundation.NSLock public struct LogHandlerWithGlobalLogLevelOverride: LogHandler { // the static properties hold the globally overridden log level (if overridden) private static let overrideLock = NSLock() private static var overrideLogLevel: Logger.Level? = nil // this holds the log level if not overridden private var _logLevel: Logger.Level = .info // metadata storage public var metadata: Logger.Metadata = [:] public init(label: String) { // [...] } public var logLevel: Logger.Level { // when we get asked for the log level, we check if it was globally overridden or not get { LogHandlerWithGlobalLogLevelOverride.overrideLock.lock() defer { LogHandlerWithGlobalLogLevelOverride.overrideLock.unlock() } return LogHandlerWithGlobalLogLevelOverride.overrideLogLevel ?? self._logLevel } // we set the log level whenever we're asked (note: this might not have an effect if globally // overridden) set { self._logLevel = newValue } } public func log(level: Logger.Level, message: Logger.Message, metadata: Logger.Metadata?, source: String, file: String, function: String, line: UInt) { // [...] } public subscript(metadataKey metadataKey: String) -> Logger.Metadata.Value? { get { return self.metadata[metadataKey] } set(newValue) { self.metadata[metadataKey] = newValue } } // this is the function to globally override the log level, it is not part of the `LogHandler` protocol public static func overrideGlobalLogLevel(_ logLevel: Logger.Level) { LogHandlerWithGlobalLogLevelOverride.overrideLock.lock() defer { LogHandlerWithGlobalLogLevelOverride.overrideLock.unlock() } LogHandlerWithGlobalLogLevelOverride.overrideLogLevel = logLevel } }
Please note that the above
See moreLogHandler
will still pass the ‘log level is a value’ test above it iff the global log level has not been overridden. And most importantly it passes the requirement listed above: A change to the log level on oneLogger
should not affect the log level of anotherLogger
variable.Declaration
Swift
public protocol LogHandler
- The