https://github.com/richardpiazza/occurrence
A swift logging library that integrates with `SwiftLog`
https://github.com/richardpiazza/occurrence
logger logging swift
Last synced: 4 months ago
JSON representation
A swift logging library that integrates with `SwiftLog`
- Host: GitHub
- URL: https://github.com/richardpiazza/occurrence
- Owner: richardpiazza
- License: mit
- Created: 2021-01-27T12:42:15.000Z (over 5 years ago)
- Default Branch: main
- Last Pushed: 2025-06-13T20:42:48.000Z (about 1 year ago)
- Last Synced: 2025-08-18T15:48:36.513Z (11 months ago)
- Topics: logger, logging, swift
- Language: Swift
- Homepage:
- Size: 94.7 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Occurrence
A multi-platform log handler for [swift-log](https://github.com/apple/swift-log).
[](https://swiftpackageindex.com/richardpiazza/Occurrence)
[](https://swiftpackageindex.com/richardpiazza/Occurrence)
## Usage
> 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`:
```swift
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:
```swift
@main
struct MyApp: App {
init() {
Occurrence.bootstrap()
// … continued initialization.
}
}
```
That's everything needed to start capturing logs!
## Queries & Exports
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.
### LogStreamer
```swift
protocol LogStreamer: Sendable {
var stream: AsyncStream { 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:
```swift
let task = Task {
for await entry in Occurrence.logStreamer.stream {
// process entry
}
}
```
### LogProvider
```swift
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.
## Conveniences
### `LogView`
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.
### `LoggableError`
A `LoggableError` is an `Error` type that can be easily converted to `Logger.Metadata`.
Many standard error types have conformances for this protocol, including:
* `CocoaError`
* `DecodingError`
* `EncodingError`
* `URLError`
There are also extensions to the `Logger` instance that allow for passthrough of a `LoggableError` instance:
```swift
@LazyLogger("MyApp") var logger: Logger
enum AppError: LoggableError {
case badData
}
func throwingFunction() throws {
guard condition else {
throw logger.error("Condition not met.", AppError.badData)
}
}
```
### `LazyLogger`
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`).
```swift
@LazyLogger("LoggerLabel") var logger: Logger
```