XuqmGroup-iOSSDK/Sources/XuqmBugCollectSDK/IssueEvent.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

229 行
6.4 KiB
Swift

import Foundation
/// Issue
public struct IssueEvent: Codable, Sendable {
public let level: String
public let platform: String
public let fingerprint: String
public let appKey: String
public let exception: ExceptionInfo?
public let breadcrumbs: [Breadcrumb]
public let release: String
public let environment: String
public let userId: String?
public let user: UserInfo?
public let device: DeviceInfo?
public let tags: [String: AnyCodable]?
public let sdk: SdkInfo?
public init(
level: String,
platform: String,
fingerprint: String,
appKey: String,
exception: ExceptionInfo? = nil,
breadcrumbs: [Breadcrumb] = [],
release: String,
environment: String,
userId: String? = nil,
user: UserInfo? = nil,
device: DeviceInfo? = nil,
tags: [String: Any?]? = nil,
sdk: SdkInfo? = nil
) {
self.level = level
self.platform = platform
self.fingerprint = fingerprint
self.appKey = appKey
self.exception = exception
self.breadcrumbs = breadcrumbs
self.release = release
self.environment = environment
self.userId = userId
self.user = user
self.device = device
self.tags = tags?.mapValues { AnyCodable($0) }
self.sdk = sdk
}
public struct ExceptionInfo: Codable, Sendable {
public let type: String
public let value: String
public let stacktrace: String?
public init(type: String, value: String, stacktrace: String? = nil) {
self.type = type
self.value = value
self.stacktrace = stacktrace
}
}
public struct UserInfo: Codable, Sendable {
public let id: String
public init(id: String) {
self.id = id
}
}
public struct DeviceInfo: Codable, Sendable {
public let model: String
public let manufacturer: String
public let osName: String
public let osVersion: String
public let locale: String
public let timezone: String
public let isEmulator: Bool
public let freeMemoryMb: Int
public let buildType: String
public init(
model: String,
manufacturer: String,
osName: String,
osVersion: String,
locale: String,
timezone: String,
isEmulator: Bool,
freeMemoryMb: Int,
buildType: String
) {
self.model = model
self.manufacturer = manufacturer
self.osName = osName
self.osVersion = osVersion
self.locale = locale
self.timezone = timezone
self.isEmulator = isEmulator
self.freeMemoryMb = freeMemoryMb
self.buildType = buildType
}
}
public struct SdkInfo: Codable, Sendable {
public let name: String
public let version: String
public init(name: String, version: String) {
self.name = name
self.version = version
}
}
}
///
public struct Breadcrumb: Codable, Sendable {
public let timestamp: Int
public let category: String
public let message: String
public let level: String
public let data: [String: AnyCodable]?
public init(
timestamp: Int,
category: String,
message: String,
level: String,
data: [String: Any?]? = nil
) {
self.timestamp = timestamp
self.category = category
self.message = message
self.level = level
self.data = data?.mapValues { AnyCodable($0) }
}
}
///
public struct LogEvent: Codable, Sendable {
public let name: String
public let properties: [String: AnyCodable]?
public let appKey: String
public let userId: String
public let platform: String
public let release: String
public let environment: String
public let sdk: IssueEvent.SdkInfo
public init(
name: String,
properties: [String: Any?]? = nil,
appKey: String,
userId: String,
platform: String,
release: String,
environment: String,
sdk: IssueEvent.SdkInfo
) {
self.name = name
self.properties = properties?.mapValues { AnyCodable($0) }
self.appKey = appKey
self.userId = userId
self.platform = platform
self.release = release
self.environment = environment
self.sdk = sdk
}
}
///
public enum LogLevel: Int, Sendable {
case debug = 0
case info = 1
case warn = 2
case error = 3
case none = 4
}
/// AnyCodable
public struct AnyCodable: Codable, Sendable {
public let value: Any
public init(_ value: Any?) {
self.value = value ?? ()
}
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if container.decodeNil() {
self.value = ()
} else if let bool = try? container.decode(Bool.self) {
self.value = bool
} else if let int = try? container.decode(Int.self) {
self.value = int
} else if let double = try? container.decode(Double.self) {
self.value = double
} else if let string = try? container.decode(String.self) {
self.value = string
} else if let array = try? container.decode([AnyCodable].self) {
self.value = array.map { $0.value }
} else if let dict = try? container.decode([String: AnyCodable].self) {
self.value = dict.mapValues { $0.value }
} else {
self.value = ()
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch value {
case is Void:
try container.encodeNil()
case let bool as Bool:
try container.encode(bool)
case let int as Int:
try container.encode(int)
case let double as Double:
try container.encode(double)
case let string as String:
try container.encode(string)
case let array as [Any]:
try container.encode(array.map { AnyCodable($0) })
case let dict as [String: Any]:
try container.encode(dict.mapValues { AnyCodable($0) })
default:
try container.encodeNil()
}
}
}