在没有 dSYM 文件的情况下从您的临时构建自动发送崩溃报告
我想分享我的解决方案,该解决方案从我们的内部构建发送符号化的崩溃日志无需 dSYM 文件。因此,您无需每次在内部创建和部署过早的应用程序时都保存 dSYM 文件。并且您无需在仅调试构建时打扰诸如 HockeyApp 或 Crashlytics 之类的崩溃报告服务。
我想出了一个崩溃报告管道,它可以自动将符号化的崩溃报告发布到 Slack 频道。下次启动应用程序时会发布此消息,因此发生崩溃后的下一次运行。所有这一切都在后台发生,而不会打扰已经烦恼的用户(最后他的应用程序崩溃了!)。
应用崩溃时的样子
崩溃报告
链接指向崩溃报告本身,我的 iOS 应用程序在后台自动上传为文本文件以及提到的 Slack 消息:
Build timestamp: Thu Oct 8 11:42:11 CEST 2015
Git commit hash: master-d675928
Incident Identifier: 4F9F2147-CEB3-426C-A122-CF215EB8DC16
CrashReporter Key: TODO
Hardware Model: iPad2,5
Process: Nuimo [4997]
Path: /private/var/mobile/Containers/Bundle/Application/9CE594BE-D9E7-4070-81FF-B1554C52CB2C/Nuimo.app/Nuimo
Identifier: com.senic.Nuimo
Version: 1
Code Type: ARM
Parent Process: launchd [1]
Date/Time: 2015-10-08 16:52:49 +0000
OS Version: iPhone OS 8.4.1 (12H321)
Report Version: 104
Exception Type: SIGTRAP
Exception Codes: #0 at 0x92c8b4
Crashed Thread: 0
Thread 0 Crashed:
0 libswiftCore.dylib 0x0092c8b4 _TTSf4s_s_d_d___TFSs18_fatalErrorMessageFTVSs12StaticStringS_S_Su_T_ + 72
1 Nuimo 0x000c80b4 _TFC5Nuimo33NuimoDiscoveryTableViewController11viewDidLoadfS0_FT_T_ + 608
2 Nuimo 0x000c8204 _TToFC5Nuimo33NuimoDiscoveryTableViewController11viewDidLoadfS0_FT_T_ + 56
3 UIKit 0x2d46f55d <redacted> + 600
4 UIKit 0x2d46f2cd <redacted> + 24
5 UIKit 0x2d9e97ad <redacted> + 2364
6 UIKit 0x2d9ea65d <redacted> + 256
7 Nuimo 0x000d2070 _TFC5Nuimo30NuimoDetailsPageViewController18showNuimoSelectionfS0_FT_T_ + 560
8 Nuimo 0x001750f4 _TPA21 + 32
9 Nuimo 0x001a0f80 _TTRXFo__dT__XFo_iT__iT__ + 16
10 Nuimo 0x0017133c _TPA__TTRXFo__dT__XFo_iT__iT__3 + 76
11 Nuimo 0x00121c64 _TFC5Nuimo6TabBar16setSelectedIndexfS0_FTGSqSi_8animatedSb_T_ + 1972
12 Nuimo 0x0011fec0 _TFC5Nuimo6TabBars13selectedIndexGSqSi_ + 64
13 Nuimo 0x00122e98 _TFC5Nuimo6TabBar12didTapButtonfS0_FCSo8UIButtonT_ + 132
14 Nuimo 0x00122f4c _TToFC5Nuimo6TabBar12didTapButtonfS0_FCSo8UIButtonT_ + 100
15 UIKit 0x2d4a082b <redacted> + 70
16 UIKit 0x2d4a07d1 <redacted> + 44
17 UIKit 0x2d48b375 <redacted> + 584
18 UIKit 0x2d4a8c9b <redacted> + 258
19 UIKit 0x2d49fe15 <redacted> + 316
20 UIKit 0x2d4997f1 <redacted> + 540
21 UIKit 0x2d46f9b5 <redacted> + 196
22 UIKit 0x2d6e60ff <redacted> + 14538
23 UIKit 0x2d46e3b7 <redacted> + 1350
24 CoreFoundation 0x29dd400f <redacted> + 14
25 CoreFoundation 0x29dd3423 <redacted> + 222
26 CoreFoundation 0x29dd1aa1 <redacted> + 768
27 CoreFoundation 0x29d1d6d1 CFRunLoopRunSpecific + 476
28 CoreFoundation 0x29d1d4e3 CFRunLoopRunInMode + 106
29 GraphicsServices 0x316b91a9 GSEventRunModal + 136
30 UIKit 0x2d4cf445 UIApplicationMain + 1440
31 Nuimo 0x0013e134 main + 164
32 libdyld.dylib 0x38b5baaf <redacted> + 2
(Rest stripped...)
那么我从崩溃报告中学到了什么?它向我展示了:
- 构建时间戳
- 构建的 git 提交哈希(非常方便地重现构建)
- 一些设备信息
- 错误堆栈跟踪——应用程序实际上在类
NuimoDiscoveryTableViewController、方法viewDidLoad 中崩溃。不幸的是没有行号,但这比没有更重要!
如何让您的临时构建发送一个符号化的崩溃报告
如果您想在您的 iOS 应用中实现相同的管道,这里有一个操作指南:
1。将 PLCrashReporter 包含到您的应用程序中
PLCrashReporter is a free open-source crash reporting library 由 HockeyApp 等付费服务使用。它允许在设备上对错误堆栈跟踪进行符号化。
假设您使用的是 CocoaPods,请在您的 Podfile 中包含:
pod 'PLCrashReporter'
如果你使用的是 Swift add these header files to your bridging header:
#import "PLCrashReport.h"
#import "PLCrashReporter.h"
#import "PLCrashReportTextFormatter.h"
2。在您的应用启动时启动您的自定义崩溃报告器
在您的应用完成启动后运行崩溃报告器。这样,崩溃报告器可以首先发送上次应用程序运行的任何未决崩溃报告,并启用 PLCrashReporter 来符号化和存储崩溃报告以备将来失败。
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
#ifdef DEBUG
CrashReporter.sharedReporter.applicationDidFinishLaunching()
...
#end
}
如您所见,崩溃报告器仅针对 Debug 配置设置,因为明确不建议将其用于发布版本。不仅因为发生崩溃时符号化需要几秒钟的时间。这就是说,add a compiler flag for your debug builds。
3。实施您的自定义崩溃报告管道
class CrashReporter {
static let sharedReporter = CrashReporter()
func applicationDidFinishLaunching() {
let crashReporter = PLCrashReporter(configuration: PLCrashReporterConfig(signalHandlerType: PLCrashReporterSignalHandlerType.BSD, symbolicationStrategy: PLCrashReporterSymbolicationStrategy.All))
if crashReporter.hasPendingCrashReport() {
if let crashData = crashReporter.loadPendingCrashReportData(), let crashReport = try? PLCrashReport(data: crashData) {
let gitCommitHash = "Git commit hash: " + ((NSBundle.mainBundle().infoDictionary ?? [:])["GitCommitHash"] as? String ?? "(not set)")
let buildTimestamp = "Build timestamp: " + ((NSBundle.mainBundle().infoDictionary ?? [:])["BuildTimestamp"] as? String ?? "(not set)")
let message = PLCrashReportTextFormatter.stringValueForCrashReport(crashReport, withTextFormat: PLCrashReportTextFormatiOS)
sendCrashReport("\(buildTimestamp + "\n" + gitCommitHash + "\n" + message)")
}
crashReporter.purgePendingCrashReport()
}
if !crashReporter.enableCrashReporter() {
print("Cannot enable crash reporter")
}
}
func sendCrashReport(message: String) {
// Now send the crash report to Slack, Twitter, Facebook, via Email, ...
}
确保最后启用PLCrashReporter!
4。附加步骤:包括构建时间戳和 git 分支/标签以及用于构建的提交哈希
我对@987654336@ 的实现包括构建时间戳、git 分支/标签和提交哈希。这使我能够轻松地重现错误的代码库。以下是包含这些附加信息的方法。然而,没有必要让上面的示例实现运行。
在 Project -> Build Phases 下添加一个新的 Run Script Phase 并粘贴这个写入构建时间和 git 详细信息的 bash 脚本进入你项目的Info.plist:
echo "Writing build time and git commit hash to Info.plist"
git_branch=$(git symbolic-ref --short -q HEAD)
git_tag=$(git describe --tags --exact-match 2>/dev/null)
git_branch_or_tag="${git_branch:-${git_tag}}"
git_hash=$(git log -1 --format="%h")
build_time=$(date)
echo "${BUILT_PRODUCTS_DIR}/${EXECUTABLE_FOLDER_PATH}/Info.plist"
echo "${git_branch_or_tag}-${git_hash}"
info_plist="${BUILT_PRODUCTS_DIR}/${EXECUTABLE_FOLDER_PATH}/Info.plist"
/usr/libexec/PlistBuddy -c "Set :GitCommitHash '${git_branch_or_tag}-${git_hash}'" "${info_plist}"
/usr/libexec/PlistBuddy -c "Set :BuildTimestamp '${build_time}'" "${info_plist}"
似乎有必要事先将这些 plist 键的两个空键值对添加到您的 Info.plist 中。添加一个名为BuildTimestamp 和另一个名为GitCommitHash。两种类型都是String。
5。让您的应用崩溃!
现在在您的代码中放置一个assertionFailure() 并运行您的应用程序。确保在未连接 Xcode 时运行应用程序,否则 Xcode 将在崩溃报告器做出反应之前处理此错误。当您成功使您的应用崩溃时,无论是否将 Xcode 连接到您的应用,您都应该重新启动它,您应该会看到崩溃报告器生成崩溃报告消息,如本答案的介绍中所示。