.package(url: "https://github.com/spydercapriani/diary-log.git", from: "0.1.0"),
Diary is designed to work out of the box, you can use the pre-configured logger anytime, anywhere:
Diary.debug("hi")
Diary.error("somethings went wrong")
You can quickly create a logger with just a label, it will use the predefined log handler:
let logger = Logger.diary(label: "some-label")
logger.level = .info
logger.info("hi")
The core component that makes this all work is DiaryHandler
.
The following code snippet shows how to create a DiaryHandler
and use it to bootstrap the logging system.
LoggingSystem.bootstrap { label in
/// ! to create your own `DiaryHandler`, you need the following
/// - `Modifier`: Maps which / how `Record`s will be handled.
/// - `Writer`: Describes where `Record`s will be written.
/// - `errorHandler` <Optional>: Handle any scenarios where records could not be modified / written.
let modifier = Modifiers.medium
let writer = TerminalWriter.stdout
let errorHandler = { error: Error in
print("Failed to log: \(error)")
}
return DiaryHandler(
label: label,
modifier: modifier,
writer: writer,
errorHandler: errorHandler
)
}
let logger = Logger(label: "app")
logger.error("Something went wrong")
Modifiers process log records.
A modifier can be a formatter, a filter, a transformer, or a chain of modifiers.
You can create a modifier with just a closure.
let modifier = Modifiers.base
.map {
"\($0.entry.level.emoji) \($0.entry.message)\n"
}
// Output: // // 🔷 hello
or via keyPaths:
let modifier = Modifiers.base
.format(
\.level.emoji,
\.message.description,
separator: " "
)
// Output:
//
// 🔷 hello
Or use the built-in modifiers directly.
Modifiers.short // default for Diary logger.
// Output:
//
// 🔷 ▶ Attempting to connect to DB
// ❌ ▶ Could not connect to DB
Modifiers.medium
// Output:
//
// 🔷 ▶ INFO ▶ label ▶ source ▶ Attempting to connect to DB
// ❌ ▶ ERROR ▶ label ▶ source ▶ Could not connect to DB
Modifiers.long
// Output:
//
// 🔷 ▶ INFO ▶ diary ▶ MyApp ▶ someFunction(_:) ▶ <Line#>
// Message: Attempting to connect to DB
// Metadata:
// --db_destination=example-destination
// --token=example-token
Modifiers.jsonString
// Output:
//
// {
// "file" : "/diary-log/Sources/Playground/main.swift",
// "function" : "demo(_:)",
// "label" : "com.playground.json",
// "level" : "info",
// "line" : 15,
// "message" : "OS Log Sample",
// "source" : "Playground"
// },
You can create a filter modifier with just a closure.
let filtered = Modifiers.short
.filter {
$0.entry.source != "Diary"
}
// logs from `Diary` will not be output.
DSL
Or use the built-in expressive dsl to create one.
let filtered = Modifiers.short
.when(\.entry.source) // Utilizes Record keypath's
.equals("Diary")
.deny
let filtered = Modifiers.short
.when(\.entry.level)
.greaterThanOrEqualTo(.error)
.allow
Modifiers are chainable, a modifier
can concatenate another modifier
.
let modifier = modifierA + modifierB + modifierC // + modifierD + ...
// or
let modifier = modifierA
.concat(modifierB)
.concat(modifierC)
// .concat(modifierD) ...
Diary ships with many commonly used operators.
Prefix & Suffix
let modifier = Modifiers.base
.format(\.message.description)
.prefix("🎈 ")
// Output:
//
// 🎈 Hello World!
let modifier = Modifiers.base
.format(\.message.description)
.suffix(" 🎈")
// Output:
//
// Hello World! 🎈
Encode Encodes Encodable records
let encoded = Modifiers.base
.encode(using: JSONEncoder())
// Or feel free to use the prebuilt modifer
let encoded = Modifers.jsonData
Encrypt Encrypts Data records
let modifier = Modifiers.base
.encode(using: JSONEncoder())
.encrypt(using: key, cipher: .ChaChaPoly)
Compress Compresses Data records
let modifier = Modifiers.base
.encode(using: JSONEncoder())
.compress(using: .COMPRESSION_LZFSE)
You may wish to add custom Context
to records to capture additional data points.
You can use Context
protocol with append
to capture and pass the context into the records Entry.Metadata
.
private struct CustomContext: Context {
let date = Date()
let thread = LogHelper.thread
}
let encodedLogger2 = Logger(label: "com.playground.encoder2") { label in
struct Custom: Context {
let info = "Some Custom Info"
let date = Date()
}
let encoder = JSONEncoder()
encoder.dateEncodingStrategy = .iso8601
encoder.outputFormatting = [.prettyPrinted, .sortedKeys, .withoutEscapingSlashes]
let modifier = Modifiers.base
.append(Custom())
.encode(using: encoder)
.stringValue()
.commaSeparated
.newLine
return DiaryHandler(
label: label,
modifier: modifier,
writer: TerminalWriter.stdout,
errorHandler: errorHandler
)
}
// Output
// {
// "category" : "example-category",
// "custom" : {
// "date" : "2022-01-27T00:23:15Z",
// "info" : "Some Custom Info"
// },
// "file" : "/diary-log/Sources/Playground/main.swift",
// "function" : "demo(_:)",
// "label" : "com.playground.encoder2",
// "level" : "info",
// "line" : 32,
// "message" : "OS Log Sample",
// "source" : "Playground",
// "subsystem" : "example-subsystem"
// },
Writers handle destinations for log records.
OSWriter
Write strings to the underlying OSLog
.
let writer = OSWriter()
TerminalWriter
Write strings to the underlying TextOutputStream
.
let writer = TerminalWriter(stream)
let stdout = TerminalWriter.stdout
let stderr = TerminalWriter.stderr
MultiplexLogHandler Support
Write records to multiple destinations.
let multiLogger = Logger(
label: label,
modifier: modifier,
TerminalWriter.stdout.eraseToAnyWriter(), // Due to type handling you must .eraseToAnyWriter()
OSWriter().eraseToAnyWriter() // Note that all writers must have the same Output type.
)