A multi-platform log handler for swift-log.
SwiftLog provides a unified, performant, and ergonomic logging API that can be adopted by libraries and applications across the Swift ecosystem.
The thing SwiftLog does not provide is a LogHandler - any instance that consumes logs being generated by libraries and applications.
This is where Occurrence steps in. This package provides a SQLite backed storage mechanism to capture, query, and export logs.
In order to provide this functionality, a LogHandler must first be bootstrapped into the logging system.
Occurrence provides a convenience method to configure itself as the LogHandler:
Occurrence.bootstrap()Bootstrapping must be completed before the initialization of any Logger instance.
Typically this is done at the very start of your application. For instance:
@main
struct MyApp: App {
init() {
Occurrence.bootstrap()
// … continued initialization.
}
}That's everything needed to start capturing logs!
Capturing logs is great, but somewhat useless if you can't do anything with them.
To allow for observation, querying, and export of logging data, Occurrence defines LogStreamer and LogProvider instances.
protocol LogStreamer: Sendable {
var stream: AsyncStream<Logger.Entry> { get }
func log(_ entry: Logger.Entry)
}A LogStream provides an AsyncStream of Logger.Entry types as they are processed by framework.
These entries can easily be observed:
let task = Task {
for await entry in Occurrence.logStreamer.stream {
// process entry
}
}protocol LogProvider: Sendable {
func log(_ entry: Logger.Entry)
func subsystems() -> [Logger.Subsystem]
func entries(matching filter: Logger.Filter?, ascending: Bool, limit: UInt) -> [Logger.Entry]
func purge(matching filter: Logger.Filter?)
}A LogProvider consumes log entries as they are registered and provides data about those entries as requested.
Various filters can be provided to fine-tune the results you're looking for.
If you are in a SwiftUI environment the LogView view provides a way to observe, manage, and export log entries.
This offers a prime example of how the LogStreamer and LogProvider types can be utilized within an application.
A LoggableError is an Error type that can be easily converted to Logger.Metadata.
Many standard error types have conformances for this protocol, including:
CocoaErrorDecodingErrorEncodingErrorURLError
There are also extensions to the Logger instance that allow for passthrough of a LoggableError instance:
@LazyLogger("MyApp") var logger: Logger
enum AppError: LoggableError {
case badData
}
func throwingFunction() throws {
guard condition else {
throw logger.error("Condition not met.", AppError.badData)
}
}Every Logger instance supplies a label (Internally Occurrence uses the type Logger.Subsystem for this).
As a convenience to creating a Logger reference, use the LazyLogger property wrapper which will create a Logger with the specific label (Logger.Subsystem).
@LazyLogger("LoggerLabel") var logger: Logger