该项目提供了一个记录器 (logger) 的实现,用于跟踪用户行为和系统行为。通过该实现,许多与日志相关的流程可以被标准化和隐藏。
这在以下情况下特别有用:
如果您正在使用 Xcode 项目,您可以通过从 Xcode 指定此存储库来为此软件包添加依赖项。
如果您正在使用 Swift Package 项目,您可以通过将以下描述添加到 Package.swift 来为此软件包添加依赖项。
dependencies: [
.product(name: "ParchmentCore", package: "Parchment"),
// The following statements are optional
.product(name: "Parchment", package: "Parchment"),
]
它包含日志处理和事件日志定义的主要逻辑和定义。
提供符合 ParchmentCore 提供的协议的标准实现。 如果您实现自己的缓冲区和调度器,则无需添加任何依赖项。
有关更多详细信息,请参见 自定义 部分。
这是一个实验性 API,可从以自然语言编写的事件日志规范生成 Swift 代码。
有关更多详细信息,请参见 文档 部分。
本节介绍该项目的基本用法。
// with struct
struct Event: Loggable {
public let eventName: String
public let parameters: [String : Any]
}
// with enum
enum Event: Loggable {
case impletion(screen: String)
var eventName: String {
...
}
var parameters: [String : Any] {
...
}
}
或者,有两种方法可以在不定义日志事件的情况下执行此操作。
TrackingEvent
使用 LoggerComponent 包装现有日志记录器实现,例如 FirebaseAnalytics 和端点。
extension LoggerComponentID {
static let analytics = LoggerComponentID("Analytics")
}
struct Analytics: LoggerComponent {
static let id: LoggerComponentID = .analytics
func send(_ event: Loggable) async -> Bool {
let url = URL(string: "https://your-endpoint/...")!
request.httpBody = convertBody(from: event)
return await withCheckedContinuation { continuation in
let task = URLSession.shared.dataTask(with: request) { data, response, error in
if let error = error {
print(error)
continuation.resume(returning: false)
return
}
guard
let response = response as? HTTPURLResponse,
(200..<300).contains(response.statusCode)
else {
continuation.resume(returning: false)
return
}
continuation.resume(returning: true)
}
task.resume()
}
}
}
初始化 LoggerBundler
并使用它发送日志。
let analytics = Analytics()
let logger = LoggerBundler(components: [analytics])
await logger.send(
TrackingEvent(eventName: "hoge", parameters: [:]),
with: .init(policy: .immediately)
)
await logger.send(.impletion(screen: "Home"))
await logger.send([\.eventName: "tapButton", \.parameters: ["ButtonID": 1]])
请参阅下面的 API 文档(WIP)。
本节介绍如何自定义记录器的行为。
Mutation 将一个日志转换为另一个日志。
如果您希望将参数添加到所有日志,这将非常有用。
要创建类型并将其设置为记录器,请按如下方式编写。
// An implementation similar to this can be found in Parchment
struct DeviceDataMutation: Mutation {
private let deviceParams = [
"Model": UIDevice.current.name,
"OS": UIDevice.current.systemName,
"OS Version": UIDevice.current.systemVersion
]
public func transform(_ event: Loggable, id: LoggerComponentID) -> Loggable {
let log: LoggableDictonary = [
\.eventName: event.eventName,
\.parameters: event.parameters.merging(deviceParams) { left, _ in left }
]
return log
}
}
logger.mutations.append(DeviceDataMutation())
LoggerComponentID 是唯一标识记录器的 ID。
通过扩展 LoggerComponentID,可以如下所示控制日志的目标位置。
extension LoggerComponentID {
static let firebase: Self = .init("firebase")
static let myBadkend: Self = .init("myBadkend")
}
await logger.send(.tap, with: .init(scope: .exclude([.firebase, .myBadkend])))
await logger.send(.tap, with: .init(scope: .only([.myBadkend])))
BufferedEventFlushScheduler 确定获取缓冲区中日志数据的时间。 要创建类型并将其设置为记录器,请按如下方式编写。
// An implementation similar to this can be found in Parchment
final class RegularlyPollingScheduler: BufferedEventFlushScheduler {
public static let `default` = RegularlyPollingScheduler(timeInterval: 60)
let timeInterval: TimeInterval
var lastFlushedDate: Date = Date()
private weak var timer: Timer?
public init(
timeInterval: TimeInterval,
) {
self.timeInterval = timeInterval
}
public func schedule(with buffer: TrackingEventBufferAdapter) async -> AsyncThrowingStream<[BufferRecord], Error> {
return AsyncThrowingStream { continuation in
let timer = Timer(fire: .init(), interval: 1, repeats: true) { _ in
Task { [weak self] in
await self?.tick(with: buffer) {
continuation.yield($0)
}
}
}
RunLoop.main.add(timer, forMode: .common)
self.timer = timer
}
}
public func cancel() {
timer?.invalidate()
}
private func tick(with buffer: TrackingEventBufferAdapter, didFlush: @escaping ([BufferRecord])->()) async {
guard await buffer.count() > 0 else { return }
let flush = {
let records = await buffer.load()
didFlush(records)
}
let timeSinceLastFlush = abs(self.lastFlushedDate.timeIntervalSinceNow)
if self.timeInterval < timeSinceLastFlush {
await flush()
self.lastFlushedDate = Date()
return
}
}
}
let logger = LoggerBundler(
components: [...],
buffer: TrackingEventBuffer = ...,
loggingStrategy: BufferedEventFlushScheduler = RegularlyPollingScheduler.default
)
TrackingEventBuffer 是一个保存日志的缓冲区。
Parchment 定义了一个类 SQLiteBuffer,它使用 SQLite 来存储日志。
此实现可以替换为与 TrackingEventBuffer 兼容的类。