LogDog 设计为开箱即用,您可以随时随地使用预配置的 logger
sugar.debug("hi")
sugar.error("somethings went wrong")
或者,制作本地副本并进行一些更改
var logger = suggar
logger["path"] = "/me"
logger.debug("hi")
您只需使用一个标签即可快速创建一个 logger,它将使用预定义的日志处理器
var logger = Logger.sugar("worker:a")
logger.level = .info
logger.info("hi")
使这一切工作的核心组件是 SugarLogHandler
。
以下代码片段展示了如何创建 SugarLogHandler
并使用它来引导日志系统。
LoggingSystem.bootstrap { label in
// ! to create your own `SugarLogHandler`, you need a `sink`, an `appender` and an optional `errorHandler`.
let sink = LogSinks.Builtin.short
let appender = TextLogAppender.stdout
let errorHandler = { error: Error in
print("LogError: \(error)")
}
var handler = SugarLogHandler(label: label, sink: sink, appender: appender, errorHandler: errorHandler)
// ! use dynamicMetadata to register values that are evaluted on logging.
handler.dynamicMetadata["currentUserId"] = {
AuthService.shared.userId
}
return handler
}
let logger = Logger(label: "app")
logger.error("Something went wrong")
Sinks (接收器) 处理日志记录。
一个 sink (接收器) 可以是一个格式化器、一个过滤器、一个 hook (钩子) 或其他 sinks (接收器) 的链。
您只需使用一个闭包即可创建一个格式化器 sink (接收器)。
let sink = LogSinks.firstly
.format {
"\($0.entry.level.uppercased) \($0.entry.message)\n"
}
// Output:
//
// DEBUG hello
或者直接使用内置的简洁格式化器。
let short = LogSinks.BuiltIn.short
// Output:
//
// E: bad response
// C: can not connect to db
let medium = LogSinks.BuiltIn.medium // default sink for sugar loggers
// Output:
//
// 20:40:56.850 E/App main.swift.39: bad response url=/me, status_code=404
// 20:40:56.850 C/App main.swift.41: can not connect to db
let long = LogSinks.BuiltIn.long
// Output:
//
// ╔════════════════════════════════════════════════════════════════════════════════
// ║ 2020-11-15 20:46:31.157 App ERROR (main.swift:39 run(_:))
// ╟────────────────────────────────────────────────────────────────────────────────
// ║ bad response
// ╟────────────────────────────────────────────────────────────────────────────────
// ║ url=/me
// ║ status_code=404
// ╚════════════════════════════════════════════════════════════════════════════════
您只需使用一个闭包即可创建一个过滤器 sink (接收器)。
let sink = LogSinks.firstly
.filter {
$0.entry.source != "LogDog"
}
// logs from `LogDog` will not be output.
DSL
或者使用内置的富有表现力的 dsl 来创建一个。
let sink = LogSinks.BuiltIn.short
.when(.path)
.contains("private")
.deny
let sink = LogSinks.BuiltIn.short
.when(.level)
.greaterThanOrEqualTo(.error)
.allow
Sinks (接收器) 是可链式的,一个 sink
(接收器) 可以连接另一个 sink
(接收器)。
let sink = sinkA + sinkB + sinkC // + sinkD + ...
// or
let sink = sinkA
.concat(sinkB)
.concat(sinkC)
// .concat(sinkD) ...
LogDog 附带了许多常用的操作符。
Prefix & Suffix (前缀 & 后缀)
let sink = LogSinks.BuiltIn.short
.prefix("🎈 ")
// Output:
//
// 🎈 E: bad response
let sink = LogSinks.BuiltIn.short
.suffix(" 🎈")
// Output:
//
// E: bad response 🎈
Encode (编码)
let sink = LogSinks.firstly
.encode(JSONEncoder())
Crypto (加密)
let sink = LogSinks.firstly
.encode(JSONEncoder())
.encrypt(using: key, cipher: .ChaChaPoly)
Compress (压缩)
let sink = LogSinks.firstly
.encode(JSONEncoder())
.compress(.COMPRESSION_LZFSE)
Sinks (接收器) 的处理可能很耗时,如果您不想让它减慢您的工作速度,您可以使用 Scheduler
使日志记录异步。
let sink = LogSinks.firstly
.sink(on: dispatchQueue) // or an operationQueue, or some other custom schedulers, for example, an eventLoop.
.encode(JSONEncoder()) // time-consuming processing begins.
.encrypt(using: key, cipher: .ChaChaPoly)
.compress(.COMPRESSION_LZFSE)
由于 Scheduler
,日志记录可能是异步的。
这意味着 sinking (接收) 可能在不同的上下文中,
您可以使用带有 entry.parameters
的 hook
(钩子) 来捕获和传递上下文。
private struct CustomSinkContext: LogParameterKey {
typealias Value = CustomSinkContext
let date = Date()
let thread = LogHelper.thread
}
let customSink: AnyLogSink<Void, String> = AnyLogSink {
// ! beforeSink: in the same context as the log generation.
$0.parameters[CustomSinkContext.self] = .init()
} sink: { record, next in
// ! sink: may not be in the same context as the log generation.
record.sink(next: next) { (record) -> String? in
guard let context = record.entry.parameters[CustomSinkContext.self] else {
return nil
}
let time = LogHelper.format(context.date, using: "HH:mm:ss.SSS")
let thread = context.thread
// ...
}
}
请注意,当使用 encode
(编码) 时,以字符串为键的参数也将被编码。
LogSinks.firstly
.hook {
$0.parameters["currentUserId"] = AuthService.shared.userId
}
.hook(.appName, .appVersion, .date) /* , ..., a lot of built-in hooks */
.encode(JSONEncoder())
/*
{
"label": "app",
"level": "debug",
"metadata": {
// ...
}
// ...
"currentUserId": "1",
"appName": "LogDog",
"appVersion": "0.0.1",
"date": "2020-11-19T01:12:37.001Z"
}
*/
Appenders (追加器) 是日志记录的目标地。
OSLogAppender
将字符串追加到底层的 OSLog
。
let appender = OSLogAppender(osLog)
TextLogAppender
将字符串追加到底层的 TextOutputStream
。
let appender = TextLogAppender(stream)
let stdout = TextLogAppender.stdout
let stderr = TextLogAppender.stderr
MultiplexLogAppender
将输出追加到底层的 appenders (追加器)。
// when `concurrent` is `true`, a dispatch group is used to make the appending of all appenders parallel.
let appender = MultiplexLogAppender(concurrent: true, a, b, c, d/*, ...*/)
FileLogAppender
WIP (正在进行中)
除了上面提到的 sinks (接收器)/appenders (追加器) 之外,您还可以在 LogDogCommunity 找到更多集成!
为 LogDog 的输出着色。
加密 LogDog 的输出。
将 LogDog 的输出追加到 CocoaLumberjack。
没有您想要的 sink (接收器)/appender (追加器)?欢迎贡献!
.package(url: "https://github.com/luoxiu/LogDog.git", from: "0.2.0"),
pod 'LogDog', '~> 0.2.0'