Поделиться через


Сбои Центра приложений (macOS)

Это важно

Центр приложений Visual Studio был прекращен 31 марта 2025 г., за исключением функций аналитики и диагностики, которые будут поддерживаться до 30 июня 2026 г. Подробнее.

Центр приложений Crashes автоматически создаёт журнал сбоев при каждом сбое вашего приложения. Журнал сначала записывается в хранилище устройства, и когда пользователь снова запускает приложение, отчет о сбоях будет отправлен в Центр приложений. Сбор сбоев работает как для бета-, так и для динамических приложений, т. е. отправленных в App Store. Журналы сбоев содержат ценные сведения, которые помогут устранить сбой.

Если вы еще не настроили пакет SDK в приложении, следуйте разделу "Начало работы ".

Кроме того, журналы сбоев в macOS требуют символьного обозначения, ознакомьтесь с документацией по диагностике Центра приложений , которая объясняет, как предоставить символы для приложения.

Замечание

В версии 4.0.0 Центра приложений были введены критические изменения. Ознакомьтесь с разделом Миграция на SDK App Center 4.0.0 и выше для переноса Центра приложений с предыдущих версий.

Отчеты о сбоях в расширениях

Центр приложений поддерживает отчеты о сбоях в расширениях macOS. Использование совпадает с использованием приложения контейнера.

Создание тестового сбоя

App Center Crashes предоставляет API для генерации тестового сбоя, что упрощает тестирование пакета SDK. Этот API можно использовать только в тестовых и бета-приложениях и не будет делать ничего в рабочих приложениях.

[MSACCrashes generateTestCrash];
Crashes.generateTestCrash()

Дополнительные сведения о предыдущем сбое

App Center Crashes предоставляет два API, которые дают вам больше информации в случае сбоя вашего приложения.

Приложение получило предупреждение о низкой памяти в предыдущем сеансе?

В любое время после запуска пакета SDK можно проверить, получено ли приложение предупреждение о памяти в предыдущем сеансе:

[MSACCrashes hasReceivedMemoryWarningInLastSession];
Crashes.hasReceivedMemoryWarningInLastSession

Замечание

Этот метод должен использоваться только после того как Crashes был запущен, до запуска всегда вернёт NO или false.

Замечание

В некоторых случаях устройство с низкой памятью не может отправлять события.

Произошел сбой приложения в предыдущем сеансе?

В любое время после запуска пакета SDK можно проверить, произошло ли сбой приложения в предыдущем запуске:

[MSACCrashes hasCrashedInLastSession];
Crashes.hasCrashedInLastSession

Это удобно, если вы хотите настроить поведение или пользовательский интерфейс приложения после сбоя.

Замечание

Этот метод должен использоваться только после того как MSACCrashes был запущен, до запуска всегда вернёт NO или false.

Сведения о последнем сбое

Если ваше приложение завершилось сбоем, вы можете получить детали о последнем сбое.

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

Замечание

Этот метод должен использоваться только после Crashes запуска, он всегда будет возвращать nil до запуска.

Существует множество вариантов использования для этого API, наиболее распространенный — это люди, которые вызывают этот API и реализуют пользовательские crashesDelegate.

Настройте использование функций сбоев в Центре приложений

Сервис сбоев App Center предоставляет обратные вызовы для разработчиков, позволяющие выполнять дополнительные действия перед отправкой журналов сбоев в App Center.

Чтобы добавить пользовательское поведение, необходимо использовать протокол CrashesDelegate, все его методы необязательны.

Регистрация в качестве делегата

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

Замечание

Перед вызовомAppCenter.startнеобходимо задать делегат, так как Центр приложений запускает сбои обработки сразу после запуска.

Следует ли обрабатывать аварию?

Реализуйте метод crashes:shouldProcessErrorReport: в классе, который использует протокол CrashesDelegate, если вы хотите определить, нужно ли обрабатывать конкретную ошибку. Например, может произойти сбой на уровне системы, который вы хотите игнорировать и что вы не хотите отправлять в Центр приложений.

- (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.
}

Обработанные ошибки

Центр приложений также позволяет отслеживать ошибки, используя метод 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)
}

Если конфиденциальность пользователей важна для вас, вы можете получить подтверждение пользователя перед отправкой отчета о сбоях в Центр приложений. Пакет SDK предоставляет обратный вызов, который сообщает Центру приложений Crashes ожидать подтверждения от пользователя перед отправкой отчетов о сбоях.

Если вы решили это сделать, вы несете ответственность за получение подтверждения пользователя, например с помощью диалогового окна с одним из следующих параметров: Always send, Send и Don't send. На основе входных данных вы сообщите App Center Crashes, что делать, и аварийное завершение работы будет обрабатываться соответствующим образом.

Замечание

Пакет SDK не отображает диалоговое окно для этого, приложение должно предоставить собственный пользовательский интерфейс для запроса согласия пользователя.

В следующем методе показано, как настроить обработчик подтверждения пользователя:

[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 в блоке обработчика выше, приложение должно получить разрешение пользователя и сообщение пакета SDK с результатом с помощью следующего API. Если вы используете для этого оповещение, как мы это делаем в приведенном выше примере, вы будете вызывать его на основе результата вызова (runModal).

// 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, установите NSApplicationCrashOnExceptions флаг перед инициализацией SDK, этот флаг позволяет приложению завершать выполнение при необработанных исключениях, и SDK может сообщать о них.

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

Замечание

Пакет SDK центра приложений автоматически устанавливает флаг в версиях 1.10.0 и ниже. Начиная с версии 1.11.0 этот флаг больше не устанавливается автоматически.

Отключите переадресацию вызовов методов основного класса приложения в App Center Crashes.

Пакет Crashes SDK для Центра приложений использует метод свизлинга для улучшения интеграции, автоматически перенаправляя некоторые вызовы методов основного класса приложения. Метод swizzling — это способ изменить реализацию методов во время выполнения. Если по какой-либо причине вы не хотите использовать swizzling (например, из-за определенной политики), вам следует самостоятельно переопределить методы 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
    

    Замечание

    try / catchСвифт не работает.NSException Эти исключения можно обрабатывать только в Objective-C.

  2. Откройте файл Info.plist и замените NSApplication в поле основного класса именем класса приложения ReportExceptionApplication в этом примере.

  3. Чтобы отключить ветвь в пакете SDK для Центра приложений, добавьте AppCenterApplicationForwarderEnabled ключ в файл Info.plist и задайте для параметра значение 0.

Получите информацию о статусе отправки для журнала сбоев

Иногда необходимо знать состояние сбоя приложения. Распространенный вариант использования заключается в том, что может потребоваться показать пользовательский интерфейс, который сообщает пользователям, что ваше приложение отправляет отчет о сбоях или, если ваше приложение завершает работу быстро после запуска, необходимо настроить поведение приложения, чтобы убедиться, что журналы сбоев можно отправить. Протокол CrashesDelegateопределяет три разных обратных вызова, которые можно использовать в приложении, чтобы получать уведомления о том, что происходит:

Функция обратного вызова будет вызвана перед тем, как 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 отправит их вместе с сбоем, чтобы их можно было увидеть на портале Центра приложений. Следующий обратный вызов будет вызван непосредственно перед отправкой сохранённого отчёта о сбое из предыдущих запусков приложения. Он не будет вызываться, когда произойдет сбой. Вот пример того, как прикрепить текст и изображение к отчету о сбое:

- (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 МБ. Попытка отправить большее вложение приведет к ошибке.

Включение или отключение сбоев Центра приложений во время выполнения

Вы можете включать и отключать App Center Crashes в процессе работы. Если отключить его, пакет SDK не будет выполнять отчеты о сбоях для приложения.

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

Чтобы снова включить аварийное завершение работы Центра приложений, используйте тот же API, но передайте YES/true его в качестве параметра.

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

Состояние сохраняется в хранилище устройства во время запуска приложения.

Замечание

Этот метод должен использоваться только после того, как Crashes был запущен.

Проверка включения аварийного сбоя Центра приложений

Кроме того, можно проверить, включена ли функция отслеживания сбоев в Центре приложений.

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

Замечание

Этот метод должен использоваться только после Crashes запуска, он всегда будет возвращать false до запуска.

Отключение обработки исключений Mach

По умолчанию в Центре приложений Crashes используется обработчик исключений 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])