设计崩溃监控系统
问题
如何设计一个 iOS 崩溃监控系统?
答案
架构
Crash 类型与捕获
class CrashReporter {
static func install() {
// 1. ObjC Exception
NSSetUncaughtExceptionHandler { exception in
let stack = exception.callStackSymbols.joined(separator: "\n")
CrashReporter.save(type: "exception", reason: exception.reason ?? "", stack: stack)
}
// 2. Unix Signal(EXC_BAD_ACCESS 等)
signal(SIGSEGV) { signal in
let stack = Thread.callStackSymbols.joined(separator: "\n")
CrashReporter.save(type: "signal", reason: "SIGSEGV", stack: stack)
}
signal(SIGABRT) { _ in
CrashReporter.save(type: "signal", reason: "SIGABRT", stack: "")
}
}
private static func save(type: String, reason: String, stack: String) {
let report = CrashReport(
type: type,
reason: reason,
stack: stack,
appVersion: Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "",
osVersion: UIDevice.current.systemVersion,
timestamp: Date()
)
// 写入本地文件(不能做网络请求,进程即将退出)
let data = try? JSONEncoder().encode(report)
let path = crashDirectory.appendingPathComponent(UUID().uuidString + ".crash")
try? data?.write(to: path)
}
}
符号化
# 使用 atos 符号化
atos -arch arm64 -o MyApp.app.dSYM/Contents/Resources/DWARF/MyApp -l 0x100000000 0x100012345
关键指标
| 指标 | 目标 |
|---|---|
| 崩溃率 | < 0.1%(按用户去重) |
| 捕获率 | 尽量捕获所有类型 |
| 上报延迟 | 下次启动立即上报 |
| 符号化 | 自动关联 dSYM |
常见面试问题
Q1: Signal 处理器中能做什么?
答案:只能调用 async-signal-safe 的函数(如 write、open)。不能调用 malloc、NSLog、Objective-C 方法。所以崩溃信息一般直接写文件。