App Center (iOS) 崩溃

重要

Visual Studio App Center 计划于 2025 年 3 月 31 日停用。 虽然可以继续使用 Visual Studio App Center,直到它完全停用,但你可以考虑迁移到几个建议的替代方法。

详细了解支持时间线和替代方法。

每次应用崩溃时,App Center 崩溃都会自动生成崩溃日志。 日志首先写入设备的存储,当用户再次启动应用时,故障报告将发送到 App Center。 收集崩溃适用于 beta 应用和实时应用,即提交到App Store的应用。 故障日志包含有助于修复崩溃的重要信息。

如果尚未在应用程序中设置 SDK,请按照入门部分进行操作。

此外,iOS 上的崩溃日志需要符号化,检查 App Center 诊断文档,其中介绍了如何为应用提供符号。

注意

如果已附加到调试器,SDK 不会转发任何故障日志。 确保应用崩溃时未附加调试器。

注意

若要接收正确符号化的堆栈跟踪,请确保已禁用位码。 可以在 App Center 的 iOS 符号化文档中了解有关 bitcode 的详细信息。

注意

4.0.0 App Center 版本中引入了中断性变更。 按照 迁移到 App Center SDK 4.0.0 及更高 版本部分从早期版本迁移 App Center。

扩展中的故障报告

App Center 支持 iOS 扩展中的故障报告。 用法与容器应用程序中的用法相同。

生成测试崩溃

App Center 崩溃提供了一个 API,用于生成测试崩溃,以便轻松测试 SDK。 此 API 只能在测试/beta 应用中使用,不会在生产应用中执行任何操作。

[MSACCrashes generateTestCrash];
Crashes.generateTestCrash()

获取有关上一次故障的详细信息

App Center 崩溃有两个 API,可在应用崩溃时为你提供详细信息。

应用在上一个会话中是否收到内存不足警告?

启动 SDK 后,可以随时检查应用是否在上一个会话中收到内存警告:

[MSACCrashes hasReceivedMemoryWarningInLastSession];
Crashes.hasReceivedMemoryWarningInLastSession

注意

此方法必须仅在启动后 Crashes 使用,它将始终返回 NO 或在 false 启动之前使用。

注意

在某些情况下,内存不足的设备无法发送事件。

应用在上一个会话中是否崩溃?

启动 SDK 后,可以随时检查应用是否在上一次启动中崩溃:

[MSACCrashes hasCrashedInLastSession];
Crashes.hasCrashedInLastSession

如果想要在崩溃后调整应用的行为或 UI,这将派上用场。

注意

此方法必须仅在启动后 MSACCrashes 使用,它将始终返回 NO 或在 false 启动之前使用。

有关上次崩溃的详细信息

如果你的应用以前崩溃,你可以获取有关上次崩溃的详细信息。

MSACErrorReport *crashReport = [MSACCrashes lastSessionCrashReport];
var crashReport = Crashes.lastSessionCrashReport

注意

此方法只能在启动后 Crashes 使用,它将始终在启动之前返回 nil

此 API 有许多用例,最常见的用例是调用此 API 并实现其自定义 CrashesDelegate 的人员。

自定义 App Center 崩溃的使用情况

App Center 崩溃为开发人员提供回调,以便在将崩溃日志发送到 App Center 之前和向 App Center 发送时执行其他操作。

若要添加自定义行为,需要采用 CrashesDelegate-protocol,其所有方法都是可选的。

注册为代理人

[MSACCrashes setDelegate:self];
Crashes.delegate = self

注意

必须在调用 AppCenter.start之前设置委托,因为 App Center 在启动后立即开始处理崩溃。

是否应处理崩溃?

crashes:shouldProcessErrorReport:如果要确定是否需要处理特定崩溃,在采用 CrashesDelegate-protocol 的类中实现 -method。 例如,可能存在你想要忽略的系统级崩溃,并且不希望发送到 App Center。

- (BOOL)crashes:(MSACCrashes *)crashes shouldProcessErrorReport:(MSACErrorReport *)errorReport {
  return YES; // return YES if the crash report should be processed, otherwise NO.
}
func crashes(_ crashes: Crashes, shouldProcess errorReport: ErrorReport) -> Bool {
  return true; // return true if the crash report should be processed, otherwise false.
}

处理的错误

App Center 还允许通过 方法使用已处理的异常 trackError 来跟踪错误。 应用可以选择将属性或/和附件附加到已处理的错误报告,以提供进一步的上下文。

@try {
  // Throw error.
} @catch (NSError *error) {

  // Init attachments.
  NSArray<MSACErrorAttachmentLog *> attachments = @[ MSACErrorAttachmentLog attachmentWithText:@"Hello world!" filename:@"hello.txt"] ]

  // Init properties.
  NSDictionary *properties = @{ "Category" : "Music", "Wifi" : "On" };

  // Track errors.
  [MSACCrashes trackError:error withProperties:properties attachments:attachments];
  [MSACCrashes trackError:error withProperties:properties attachments:nil];
  [MSACCrashes trackError:error withProperties:nil attachments:attachments];
  [MSACCrashes trackError:error withProperties:nil attachments:nil];
}
do {
  // Throw error.
} catch {

  // Init attachments.
  let attachments = [ErrorAttachmentLog.attachment(withText: "Hello world!", filename: "hello.txt")]

  // Init properties.
  let properties:Dictionary<String, String> = ["Category" : "Music", "Wifi" : "On"]

  // Track errors.
  Crashes.trackError(error, properties: properties, attachments: attachments)
  Crashes.trackError(error, properties: properties, attachments: nil)
  Crashes.trackError(error, properties: nil, attachments: attachments)
  Crashes.trackError(error, properties: nil, attachments: nil)
}

对于跟踪异常,可以使用 trackException 方法:

@try {
  // Throw exceptions.
} @catch (NSException *exception) {

  // Init exceptions.
  MSACExceptionModel *customException1 = [MSACExceptionModel initWithType:@"Custom exception" exceptionMessage:"Track custom exception.", stackTrace:exception.callStackSymbols];
  MSACExceptionModel *customException2 = [MSACExceptionModel initWithException:exception];

  // Track exceptions.
  [MSACCrashes trackException:customException1 withProperties:properties attachments:nil];
  [MSACCrashes trackException:customException2 withProperties:properties attachments:nil];
}
do {
  // Throw exception.
} catch {

  // Init exception.
  let exception = ExceptionModel(withType: "Custom exception", exceptionMessage: "Track custom exception.", stackTrace: Thread.callStackSymbols)

  // Track exception.
  Crashes.trackException(exception, properties: properties, attachments: nil)
}

如果用户隐私对你很重要,你可能希望在向 App Center 发送故障报告之前获得用户确认。 SDK 公开一个回调,告知 App Center 崩溃在发送任何崩溃报告之前等待用户确认。

如果选择这样做,则需负责获取用户的确认,例如,通过对话框提示,其中包含以下选项之一: 始终发送发送不发送。 根据输入,你将告知 App Center 崩溃要执行的操作,然后会相应地处理崩溃。

注意

SDK 不为此显示对话框,应用必须提供自己的 UI 才能请求用户同意。

注意

如果应用未实现用户确认对话框,则不应显式调用 notifyWithUserConfirmation ;崩溃模块将隐式处理发送日志。

以下方法演示如何设置用户确认处理程序:

[MSACCrashes setUserConfirmationHandler:(^(NSArray<MSACErrorReport *> *errorReports) {

  // Your code to present your UI to the user, e.g. an UIAlertController.
  UIAlertController *alertController = [UIAlertController
      alertControllerWithTitle:@"Sorry about that!"
                      message:@"Do you want to send an anonymous crash report so we can fix the issue?"
                preferredStyle:UIAlertControllerStyleAlert];

  [alertController
      addAction:[UIAlertAction actionWithTitle:@"Don't send"
                                        style:UIAlertActionStyleCancel
                                      handler:^(UIAlertAction *action) {
                                        [MSACCrashes notifyWithUserConfirmation:MSACUserConfirmationDontSend];
                                      }]];

  [alertController
      addAction:[UIAlertAction actionWithTitle:@"Send"
                                        style:UIAlertActionStyleDefault
                                      handler:^(UIAlertAction *action) {
                                        [MSACCrashes notifyWithUserConfirmation:MSACUserConfirmationSend];
                                      }]];

  [alertController
      addAction:[UIAlertAction actionWithTitle:@"Always send"
                                        style:UIAlertActionStyleDefault
                                      handler:^(UIAlertAction *action) {
                                        [MSACCrashes notifyWithUserConfirmation:MSACUserConfirmationAlways];
                                      }]];
  // Show the alert controller.
  [self.window.rootViewController presentViewController:alertController animated:YES completion:nil];
  return YES; // Return YES if the SDK should await user confirmation, otherwise NO.
})];
Crashes.setUserConfirmationHandler({ (errorReports: [ErrorReport]) in

  // Your code to present your UI to the user, e.g. an UIAlertController.
  let alertController = UIAlertController(title: "Sorry about that!",
                                          message: "Do you want to send an anonymous crash report so we can fix the issue?",
                                          preferredStyle:.alert)

  alertController.addAction(UIAlertAction(title: "Don't send", style: .cancel) {_ in
    Crashes.notify(with: .dontSend)
  })

  alertController.addAction(UIAlertAction(title: "Send", style: .default) {_ in
    Crashes.notify(with: .send)
  })

  alertController.addAction(UIAlertAction(title: "Always send", style: .default) {_ in
    Crashes.notify(with: .always)
  })

  // Show the alert controller.
  self.window?.rootViewController?.present(alertController, animated: true)
  return true // Return true if the SDK should await user confirmation, otherwise return false.
})

如果你在上面的处理程序块中返回 YES/true ,你的应用应获取用户权限,并使用以下 API 向 SDK 发送结果消息。 如果你为此使用警报,就像我们在上面的示例中所做的那样,你会从 -callback 的 alertView:clickedButtonAtIndex:实现中调用它。

// Depending on the users's choice, call notifyWithUserConfirmation: with the right value.
[MSACCrashes notifyWithUserConfirmation:MSACUserConfirmationDontSend];
[MSACCrashes notifyWithUserConfirmation:MSACUserConfirmationSend];
[MSACCrashes notifyWithUserConfirmation:MSACUserConfirmationAlways];
// Depending on the user's choice, call notify(with:) with the right value.
Crashes.notify(with: .dontSend)
Crashes.notify(with: .send)
Crashes.notify(with: .always)

获取有关故障日志的发送状态的信息

有时,你想知道应用崩溃的状态。 一个常见用例是,你可能想要显示 UI,告知用户你的应用正在提交崩溃报告,或者,如果你的应用在启动后快速崩溃,你希望调整应用的行为以确保可以提交崩溃日志。 CrashesDelegate-protocol 定义了三个不同的回调,你可以在应用中使用这些回调来接收所发生情况的通知:

在 SDK 发送崩溃日志之前,将调用以下回调

- (void)crashes:(MSACCrashes *)crashes willSendErrorReport:(MSACErrorReport *)errorReport {
  // Your code, e.g. to present a custom UI.
}
func crashes(_ crashes: Crashes, willSend errorReport: ErrorReport) {
  // Your code, e.g. to present a custom UI.
}

如果我们在终结点上遇到网络问题或中断,并且你重新启动应用, willSendErrorReport 则会在进程重启后再次触发。

SDK 成功发送崩溃日志后,将调用以下回调

- (void)crashes:(MSACCrashes *)crashes didSucceedSendingErrorReport:(MSACErrorReport *)errorReport {
  // Your code, e.g. to hide the custom UI.
}
func crashes(_ crashes: Crashes, didSucceedSending errorReport: ErrorReport) {
  // Your code goes here.
}

如果 SDK 无法发送故障日志,将调用以下回调

- (void)crashes:(MSACCrashes *)crashes didFailSendingErrorReport:(MSACErrorReport *)errorReport withError:(NSError *)error {
  // Your code goes here.
}
func crashes(_ crashes: Crashes, didFailSending errorReport: ErrorReport, withError error: Error) {
  // Your code goes here.
}

接收 didFailSendingErrorReport 意味着发生了不可恢复的错误,例如 4xx 代码。 例如, 401 表示 appSecret 错误。

如果这是网络问题,则不会触发此回调。 在这种情况下,SDK 会继续重试 (,并在网络连接关闭) 时暂停重试。

将附件添加到崩溃报告

可以将二进制和文本附件添加到故障报告。 SDK 将随故障一起发送它们,以便你可以在 App Center 门户中查看它们。 在从上一个应用程序启动时发送存储的故障之前,将立即调用以下回调。 崩溃发生时,不会调用它。 下面是如何将文本和图像附加到崩溃的示例:

- (NSArray<MSACErrorAttachmentLog *> *)attachmentsWithCrashes:(MSACCrashes *)crashes
                                             forErrorReport:(MSACErrorReport *)errorReport {
  MSACErrorAttachmentLog *attachment1 = [MSACErrorAttachmentLog attachmentWithText:@"Hello world!" filename:@"hello.txt"];
  MSACErrorAttachmentLog *attachment2 = [MSACErrorAttachmentLog attachmentWithBinary:[@"Fake image" dataUsingEncoding:NSUTF8StringEncoding] filename:@"fake_image.jpeg" contentType:@"image/jpeg"];
  return @[ attachment1, attachment2 ];
}
func attachments(with crashes: Crashes, for errorReport: ErrorReport) -> [ErrorAttachmentLog]? {
  let attachment1 = ErrorAttachmentLog.attachment(withText: "Hello world!", filename: "hello.txt")
  let attachment2 = ErrorAttachmentLog.attachment(withBinary: "Fake image".data(using: String.Encoding.utf8), filename: nil, contentType: "image/jpeg")
  return [attachment1!, attachment2!]
}

注意

大小限制当前为 7 MB。 尝试发送较大的附件将触发错误。

在运行时启用或禁用 App Center 崩溃

可以在运行时启用和禁用 App Center 崩溃。 如果禁用它,SDK 不会为应用执行任何崩溃报告。

[MSACCrashes setEnabled:NO];
Crashes.enabled = false

若要再次启用 App Center 崩溃,请使用同一 API,但作为参数传递 YES/true

[MSACCrashes setEnabled:YES];
Crashes.enabled = true

状态在应用程序启动时保留在设备的存储中。

注意

只有在启动后 Crashes 才能使用此方法。

检查是否启用了 App Center 崩溃

还可以检查是否启用 App Center 崩溃:

BOOL enabled = [MSACCrashes isEnabled];
var enabled = Crashes.enabled

注意

此方法只能在启动后 Crashes 使用,它始终在启动前返回 false

禁用 Mach 异常处理

默认情况下,App Center 崩溃使用 Mach 异常处理程序通过 Mach 异常服务器捕获致命信号,例如堆栈溢出。

disableMachExceptionHandler-方法提供了一个选项,用于禁用通过 Mach 异常服务器捕获致命信号。 如果要禁用 Mach 异常处理程序,则应在启动 SDK 之前 调用此方法。 典型的设置代码如下所示:

[MSACCrashes disableMachExceptionHandler];
[MSACAppCenter start:@"{Your App Secret}" withServices:@[[MSACAnalytics class], [MSACCrashes class]]];
Crashes.disableMachExceptionHandler()
AppCenter.start(withAppSecret: "{Your App Secret}", services: [Analytics.self, Crashes.self])