App Center 崩溃(macOS)

重要

Visual Studio App Center 于 2025 年 3 月 31 日停用,但分析和诊断功能除外,这些功能将继续受支持,直到 2026 年 6 月 30 日。 了解详细信息

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

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

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

注释

4.0.0 版本中引入了 App Center 的重大变更。 请按照“迁移到 App Center SDK 4.0.0 及更高”部分的指引,将 App Center 从之前的版本迁移。

扩展中的崩溃报告

App Center 支持 macOS 扩展中的崩溃报告。 用法与容器应用程序中的用法相同。

触发测试崩溃

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

[MSACCrashes generateTestCrash];
Crashes.generateTestCrash()

获取有关以前崩溃的详细信息

App Center 崩溃功能提供两个 API,当应用程序崩溃时为您提供更多信息。

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

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

[MSACCrashes hasReceivedMemoryWarningInLastSession];
Crashes.hasReceivedMemoryWarningInLastSession

注释

此方法必须在 Crashes 启动后才可使用,否则在启动之前,它将始终返回 NOfalse

注释

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

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

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

[MSACCrashes hasCrashedInLastSession];
Crashes.hasCrashedInLastSession

如果想要在发生崩溃后调整应用的行为或 UI,这非常有用。

注释

此方法必须在 MSACCrashes 启动后才可使用,否则在启动之前,它将始终返回 NOfalse

有关上次崩溃的详细信息

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

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

注释

此方法应仅在 Crashes 启动后使用,在启动前,它将始终返回 nil

此 API 有许多用例,其中最常见的是用户调用此 API 以实现他们自定义的 CrashesDelegate

自定义 App Center 崩溃功能的使用

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

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

注册为代表

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

注释

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

是否应处理故障?

在采用crashes:shouldProcessErrorReport:协议的类中实现CrashesDelegate方法,如果想要确定是否需要处理特定的崩溃。 例如,系统级崩溃您可能希望忽略,并且您不想发送到 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 才能请求用户同意。

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

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

  // Your code to present your UI to the user, e.g. an NSAlert.
  NSAlert *alert = [[NSAlert alloc] init];
  [alert setMessageText:@"Sorry about that!"];
  [alert setInformativeText:@"Do you want to send an anonymous crash report so we can fix the issue?"];
  [alert addButtonWithTitle:@"Always send"];
  [alert addButtonWithTitle:@"Send"];
  [alert addButtonWithTitle:@"Don't send"];
  [alert setAlertStyle:NSWarningAlertStyle];

  switch ([alert runModal]) {
  case NSAlertFirstButtonReturn:
    [MSACCrashes notifyWithUserConfirmation:MSACUserConfirmationAlways];
    break;
  case NSAlertSecondButtonReturn:
    [MSACCrashes notifyWithUserConfirmation:MSACUserConfirmationSend];
    break;
  case NSAlertThirdButtonReturn:
    [MSACCrashes notifyWithUserConfirmation:MSACUserConfirmationDontSend];
    break;
  default:
    break;
  }

  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 NSAlert.
  let alert: NSAlert = NSAlert()
  alert.messageText = "Sorry about that!"
  alert.informativeText = "Do you want to send an anonymous crash report so we can fix the issue?"
  alert.addButton(withTitle: "Always send")
  alert.addButton(withTitle: "Send")
  alert.addButton(withTitle: "Don't send")
  alert.alertStyle = NSWarningAlertStyle

  switch (alert.runModal()) {
  case NSAlertFirstButtonReturn:
    Crashes.notify(with: .always)
    break;
  case NSAlertSecondButtonReturn:
    Crashes.notify(with: .send)
    break;
  case NSAlertThirdButtonReturn:
    Crashes.notify(with: .dontSend)
    break;
  default:
    break;
  }

  return true // Return true if the SDK should await user confirmation, otherwise return false.
})

如果在上面的处理程序块中返回 YES/true ,您的应用应获取用户权限,然后使用以下 API 将结果发送给 SDK。 如果对此使用警报,正如我们在上面的示例中所做的那样,你会根据调用的结果(ModalResponserunModal 来调用它。

// Depending on the user'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)

将捕获主线程上抛出的未捕获异常功能启用

AppKit 捕获主线程上引发的异常,防止应用程序在 macOS 上崩溃,因此 SDK 无法捕获这些崩溃。 若要模拟 iOS 行为,请在 SDK 初始化之前设置 NSApplicationCrashOnExceptions 标志,此标志允许应用程序在未捕获的异常时崩溃,SDK 可以报告它们。

[[NSUserDefaults standardUserDefaults] registerDefaults:@{ @"NSApplicationCrashOnExceptions" : @YES }];
UserDefaults.standard.register(defaults: ["NSApplicationCrashOnExceptions": true])

注释

App Center SDK 在版本 1.10.0 及更低版本中自动设置标志。 从版本 1.11.0 开始,此标志不再自动设置。

禁用对 App Center 崩溃的应用程序主类方法调用的转发

App Center 崩溃报告 SDK 使用方法调配来改进其集成,通过将应用程序主类中的某些方法调用转发至它自己。 方法重排是一种在运行时更改方法实现的方法。 如果出于任何原因而不想使用重排(例如,由于特定策略),则应自行重写应用程序的 reportException:sendEvent: 方法,以便崩溃报告在主线程上正确引发的异常。

  1. 创建 ReportExceptionApplication.m 文件并添加以下实现:

    @import Cocoa;
    @import AppCenterCrashes;
    
    @interface ReportExceptionApplication : NSApplication
    @end
    
    @implementation ReportExceptionApplication
    
    - (void)reportException:(NSException *)exception {
      [MSACCrashes applicationDidReportException:exception];
      [super reportException:exception];
    }
    
    - (void)sendEvent:(NSEvent *)theEvent {
      @try {
        [super sendEvent:theEvent];
      } @catch (NSException *exception) {
        [self reportException:exception];
      }
    }
    
    @end
    

    注释

    Swift 的try/catch不起作用与NSException。 这些异常只能在 Objective-C 中处理。

  2. 打开 Info.plist 文件,并将 Principal 类字段中的 NSApplication 替换为应用程序类名称 ReportExceptionApplication 在本示例中。

  3. 若要在 App Center SDK 中禁用重排,请将 AppCenterApplicationForwarderEnabled 密钥添加到 Info.plist 文件,并将值 0设置为 。

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

有时,你想知道应用崩溃的状态。 常见的用例是,你可能想要显示 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-method 提供了一个选项,用于通过 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])