Scrooge
Scrooge 是一个日志客户端,它会将您想要在服务器上查看的定制日志,发送到您的服务器以跟踪用户的操作、行为等。您可以配置它将日志以 JSON 格式发送到您的服务器。
它提供的内容
持久性
新日志在成功发送到服务器之前,会保存在文件中。这使得日志即使在网络断开、服务器错误或崩溃等突发事件发生时,也能安全地稍后发送到服务器。
计划任务
您可以计划 Scrooge 以一定的间隔发送累积的日志。它也会根据很多情况比如后台和前台的切换以及网络连接来适当地运行。
内置 API
Scrooge 处理 API。您只需要设置服务器的 URL,然后它会以 JSON 格式发送日志。
无错误
Scrooge 要求您定义属性名称为枚举类型,并在构建日志时使用它们。这样可以确保您不会无意中出错。
更详细的说明
存储
- 您放入的新日志将存储到同名文件中。
- 当一些日志成功发送到服务器时,它们会相应地从文件中删除。
- 相比之下,如果 API 请求失败发送它们到服务器,由于可能的错误情况,它们将继续存在于文件中,这是未来可能解决的。
- 它通过将保存操作推迟以查看是否有新日志到来来减少 I/O 执行次数,以考虑性能。
注意:由于延迟保存策略以获得更好的性能,可能会丢失少量日志。
调度器
- 您可以设置何时将它们发送到服务器。它使用一个内部计时器,您可以为它设置周期。
- 它支持不同的间隔,以适应应用程序的前景和后台状态。
- 所有操作都在具有 .background QoS 的后台队列上运行,所以它不会影响应用程序的性能。
- 当互联网连接恢复时,它会自动发送存储的日志。
- 如果您的应用程序支持后台模式,它提供后台执行。
API
- 您只需设置服务器API的URL。然后,它就工作了。
- 您可以发送日志为json map或array之一。
- 您可以为每个请求发送到服务器的日志数量进行配置,以减轻服务器端的负担或出于某些原因。
日志
- 您可以设置各种类型的值。
- 日志将根据您定义的名称和给定的值相应地转换为json格式。
安装
CocoaPods
CocoaPods 是Cocoa项目的依赖项管理器。要使用CocoaPods安装Scrooge
-
请确保已安装 CocoaPods。
-
更新您的Podfile以包含以下内容
use_frameworks! pod 'Scrooge'
-
运行
pod install
。
- 在您的代码中导入Scrooge,如下所示:
import Scrooge
示例
示例用法
//when you add a log
Scrooge<SampleProperty>.shared.append { (log) in
log[.os_version] = "13.3"
log[.page_id] = "/home"
log[.index] = 3
log[.loggedIn] = true
}
示例
如何设置日志记录器
首先,您需要定义用作枚举类型的属性
enum SampleProperty: String, ScroogePropertyType {
case os_version
case page_id
case index
case detail
case loggedIn
}
现在,初始化日志记录器
// defined the logger as a class
// this makes code simpler as you can refer to the instance with Logger.shared
// instead of Scrooge<SampleProperty>.shared
class Logger {
static private let configuration: ScroogeConfiguration = {
var configuration = ScroogeConfiguration(fileName: "events")
configuration.timeInterval = 10 // defalut is 60 seconds
configuration.timeIntervalOnBackground = 60 // default is nil
configuration.limitedSendingCount = 2 // default is 300 logs
configuration.apiTimeout = 30 // default 30 seconds
return configuration
}()
// defined as a singleton as the Scrooge needs to be used as a singleton
// to manage logs in one place as they might be added in many different places
static let shared: Scrooge<SampleProperty> = {
Scrooge<SampleProperty>.shared.configuration = Logger.configuration
Scrooge<SampleProperty>.shared.api = ScroogeAPI(urlString: "https://example1234.com/action_log")
return Scrooge<SampleProperty>.shared
}()
}
如果您想查看详细的调试消息
// if you want to see verbose debugging messages
Scrooge<SampleProperty>.shared.verbose = .enabled(domain: "ExampleApp")
// if you want to see and also receive the verbose message, you can use this
Scrooge<SampleProperty>.shared.verbose = .enabled(domain: "ExampleApp") { (message) in
print("[From Scrooge] \(message)")
}
那么,您将看到这样的日志
[Scrooge.ExampleApp.Appending] appending logs
[Scrooge.ExampleApp.Network] Internet connected
[Scrooge.ExampleApp.Sending] sending 1 logs
[Scrooge.ExampleApp.Sending] sending started
[Scrooge.ExampleApp.Sending] sending succeeded
[Scrooge.ExampleApp.Removing] removing 1 logs
[Scrooge.ExampleApp.Saving] saving remaining 0 logs
[Scrooge.ExampleApp.Saving] saving succeeded
[Scrooge.ExampleApp.Sending] sending done. 0 logs remain
[Scrooge.ExampleApp.Appending] appending logs
[Scrooge.ExampleApp.Appending] appending logs
[Scrooge.ExampleApp.Saving] saving remaining 2 logs
[Scrooge.ExampleApp.Saving] saving succeeded
[Scrooge.ExampleApp.Timer] timerTriggered
[Scrooge.ExampleApp.Sending] sending 2 logs
[Scrooge.ExampleApp.Sending] sending started
[Scrooge.ExampleApp.Sending] sending succeeded
[Scrooge.ExampleApp.Removing] removing 2 logs
[Scrooge.ExampleApp.Saving] saving remaining 0 logs
[Scrooge.ExampleApp.Saving] saving succeeded
[Scrooge.ExampleApp.Sending] sending done. 0 logs remain
现在,您已设置完毕。您可以在任何位置通过日志记录器添加日志。
//when you add a log
Logger.shared.append { (log) in
log[.os_version] = "13.3"
log[.page_id] = "/home"
log[.index] = 3
log[.loggedIn] = true
}
关于配置的更多信息
public struct ScroogeConfiguration: ScroogeConfigurationType {
// timeInterval for periodic sendings
public var timeInterval: TimeInterval = 60
// timeInterval for periodic sendings when the app is in the background
// if it's nil, timeInterval above will be used
public var timeIntervalOnBackground: TimeInterval?
// for enabling the background execution
// your app should be supporting the background mode to enable this
public var enableBackgroundExecution: Bool = true
// limit number of logs per sending
public var limitedSendingCount: Int? = 300
public var fileName: String
// timeout for an api request
public var apiTimeout: TimeInterval? = 30
public init(fileName: String) {
self.fileName = fileName
}
}
示例
附加日志
Logger.shared.append { (log) in
log[.os_version] = "13.3"
log[.page_id] = "/home"
log[.index] = 3
log[.loggedIn] = true
}
// this will be converted to a json to be sent to the server
[
{
"index" : 3,
"page_id" : "\/home",
"os_version" : "13.3",
"loggedIn" : true
}
]
enum SampleDetailProperty: String, ScroogePropertyType {
case ids
case title
}
Logger.shared.append { (log) in
log[.os_version] = "13.3"
log[.page_id] = "/home"
log[.index] = 3
// add a log using a different enum type
var detailLog = ScroogeLog<SampleDetailProperty>()
detailLog[.ids] = [1, 2, 3]
detailLog[.title] = "scrooge log"
log[.detail] = detailLog.nestedLog
}
// this will be converted to a json like this
[
{
"detail" : {
"title" : "scrooge log",
"ids" : [
1,
2,
3
]
},
"page_id" : "\/home",
"os_version" : "13.3",
"index" : 3,
"loggedIn" : true
}
]
示例
修改API请求头值
如果您想修改请求头,请使用headerFiledBuilder属性。
var api = ScroogeAPI(urlString: "https://example1234.com/action_log")
api.headerFiledBuilder = { request in
let timeStamp = String(format:"%.0f", floor(Date().timeIntervalSince1970 * 1000))
request.setValue(timeStamp, forHTTPHeaderField: "x-gm-request-time")
if let info = Bundle.main.infoDictionary {
let appVersion = info["CFBundleShortVersionString"] as? String ?? "4.0.0"
request.setValue(appVersion, forHTTPHeaderField: "x-gm-app-version")
}
request.setValue("IOS", forHTTPHeaderField: "x-gm-os-type")
request.setValue(UIDevice.current.systemVersion, forHTTPHeaderField: "x-gm-os-version")
}
或者,如果您想自定义发送逻辑,可以通过遵守ScroogeAPIType协议来实现。
public protocol ScroogeAPIType {
var url: URL { get set }
var wrappingKey: String? { get set }
var headerFiledBuilder: ((inout URLRequest) -> ())? { get set }
var debugMessage: ScroogeDebuggMessage? { get set }
func send(_ logs: [PrimitiveLog], result: @escaping (Result<URLResponse, Error>) -> ())
}
struct CustomizedAPI: ScroogeAPIType {
// implement the protocols here
...
}
如果您想将json作为一个带有键的映射来包装,为它设置值。
var api = ScroogeAPI(urlString: "https://example1234.com/action_log")
api.wrappingKey = "root"
{
"root": [
{
"page_id": "/home",
"index": 3,
"os_version": "13.3"
},
{
"index": 3,
"page_id": "/home",
"os_version": "13.3"
}
]
}
示例
时间间隔
如果您希望当您的应用处于前台和后台时时间间隔不同,您可以使用配置来实现。
var configuration = ScroogeConfiguration(fileName: "events")
configuration.timeInterval = 10 // the sending timer will be fired every 10 second on foreground,
configuration.timeIntervalOnBackground = 60 // every one minute on background
与Scrooge兼容的类型
这些是可以设置为日志值的类型。当添加不包含在此处的值时,将会出现致命错误以向您发出警报。
protocol ScroogeSupportingValueType {}
extension Int: ScroogeSupportingValueType {}
extension Int8: ScroogeSupportingValueType {}
extension Int16: ScroogeSupportingValueType {}
extension Int32: ScroogeSupportingValueType {}
extension Int64: ScroogeSupportingValueType {}
extension UInt: ScroogeSupportingValueType {}
extension UInt8: ScroogeSupportingValueType {}
extension UInt16: ScroogeSupportingValueType {}
extension UInt32: ScroogeSupportingValueType {}
extension UInt64: ScroogeSupportingValueType {}
extension Float: ScroogeSupportingValueType {}
extension Double: ScroogeSupportingValueType {}
extension String: ScroogeSupportingValueType {}
extension Bool: ScroogeSupportingValueType {}
extension Array: ScroogeSupportingValueType where Element: ScroogeSupportingValueType {}
extension Dictionary: ScroogeSupportingValueType where Key == String, Value: Any {}
extension Optional: ScroogeSupportingValueType where Wrapped: ScroogeSupportingValueType {}
处理请求失败
Scrooge将决定哪些失败值得稍后重试。这些都是它将在下一个间隔中重试的失败。如果失败超过两次,Scroogee将删除前50个日志以查看是否有机会恢复它。
case notFound//404
case internalServer//500
case badGateway//502
case serviceUnavailable //503
如果发生任何错误(除了这些),Scroogee将跳过前50个日志,而不重试它,以查看是否有机会恢复。
感谢。