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

Важно!

Прекращение поддержки Центра приложений Visual Studio запланировано на 31 марта 2025 г. Хотя вы можете продолжать использовать Центр приложений Visual Studio до полного прекращения его использования, существует несколько рекомендуемых вариантов, на которые можно перейти.

Узнайте больше о сроках поддержки и альтернативных вариантах.

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

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

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

Примечание

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

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

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

Создание аварийного завершения теста

Сбои Центра приложений предоставляют API для создания тестового сбоя для простого тестирования пакета SDK. Этот API можно использовать только в тестовых или бета-версиях приложений и не будет выполнять никаких действий в рабочих приложениях.

[MSACCrashes generateTestCrash];
Crashes.generateTestCrash()

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

При сбоях Центра приложений есть два 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.

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

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

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

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

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

Примечание

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

Следует ли обработать сбой?

crashes:shouldProcessErrorReport:Реализуйте -method в классе , который принимает 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 предоставляет обратный вызов, который сообщает о сбоях Центра приложений о том, что перед отправкой отчетов о сбоях пользователь ожидает подтверждения.

Если вы решили сделать это, вы несете ответственность за получение подтверждения пользователя, например через диалоговое окно с одним из следующих вариантов: Всегда отправлять, Отправлять и Не отправлять. В зависимости от входных данных вы укаживаете, что нужно сделать, и сбой будет обрабатываться соответствующим образом.

Примечание

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

Включение перехвата неперехваченных исключений, создаваемых в потоке main

AppKit перехватывает исключения, создаваемые в потоке main, предотвращая сбой приложения в macOS, поэтому пакет SDK не может перехватывать эти сбои. Чтобы имитировать поведение iOS, установите NSApplicationCrashOnExceptions флаг перед инициализацией пакета SDK. Этот флаг позволяет приложению завершать работу при неперехваченных исключениях, и пакет SDK может сообщить о них.

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

Примечание

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

Отключение переадресации вызовов методов main класса приложения к сбоям Центра приложений

Пакет SDK центра приложений аварийно завершает работу, чтобы улучшить интеграцию, перенаправляя некоторые вызовы методов класса main приложения. Вращение методов — это способ изменения реализации методов во время выполнения. Если по какой-либо причине вы не хотите использовать зависание (например, из-за определенной политики), следует переопределить методы приложения reportException: и sendEvent: самостоятельно, чтобы при сбоях сообщалось об исключениях, создаваемых в потоке main.

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

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

Вы можете включить и отключить сбои Центра приложений во время выполнения. Если вы отключите его, пакет 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

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