XuqmGroup-iOSSDK/Sources/XuqmBugCollectSDK/internal/LogStorage.swift
XuqmGroup 227bc66bfd feat: 添加 iOS BugCollect 崩溃收集模块
新增模块:XuqmBugCollectSDK
- BugCollectSDK: 主入口,错误捕获 API
- CrashCapture: 崩溃捕获(NSException + POSIX 信号)
- IssueEvent/LogEvent: 数据模型
- Fingerprint: 错误指纹计算(SHA-256)
- LogQueue: 内存队列 + 定时上传
- LogStorage: 崩溃文件持久化
- LogUploader: HTTP 上传到服务端
- FunnelTracker: 漏斗追踪

功能:
- captureError/captureCrash 捕获错误
- addBreadcrumb 添加面包屑
- event 记录自定义事件
- warn/info 日志级别记录
- 自动初始化(onInitialize hook)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-19 03:38:22 +08:00

63 行
1.9 KiB
Swift

import Foundation
///
enum LogStorage {
private static let crashDir = "xuqm_bugcollect_crashes"
private static let encoder: JSONEncoder = {
let e = JSONEncoder()
e.dateEncodingStrategy = .iso8601
return e
}()
///
static func saveCrash(_ event: IssueEvent) {
let dir = crashDirectory()
try? FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true)
let filename = "\(Int(Date().timeIntervalSince1970))_\(event.fingerprint.prefix(8)).json"
let url = dir.appendingPathComponent(filename)
if let data = try? encoder.encode(event) {
try? data.write(to: url)
}
}
///
static func loadPendingCrashes() -> [IssueEvent] {
let dir = crashDirectory()
guard let files = try? FileManager.default.contentsOfDirectory(
at: dir,
includingPropertiesForKeys: nil
) else { return [] }
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
return files
.filter { $0.pathExtension == "json" }
.compactMap { url -> IssueEvent? in
guard let data = try? Data(contentsOf: url) else { return nil }
return try? decoder.decode(IssueEvent.self, from: data)
}
}
///
static func clearPendingCrashes() {
let dir = crashDirectory()
guard let files = try? FileManager.default.contentsOfDirectory(
at: dir,
includingPropertiesForKeys: nil
) else { return }
for file in files {
try? FileManager.default.removeItem(at: file)
}
}
private static func crashDirectory() -> URL {
let caches = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
return caches.appendingPathComponent(crashDir)
}
}