Bloqueos de App Center (macOS)

Importante

Visual Studio App Center está programado para retirarse el 31 de marzo de 2025. Aunque puede seguir usando Visual Studio App Center hasta que se retire por completo, hay varias alternativas recomendadas a las que puede considerar la posibilidad de migrar.

Obtenga más información sobre las escalas de tiempo y las alternativas de soporte técnico.

Los bloqueos de App Center generarán automáticamente un registro de bloqueos cada vez que se bloquee la aplicación. El registro se escribe primero en el almacenamiento del dispositivo y, cuando el usuario vuelve a iniciar la aplicación, el informe de bloqueo se enviará a App Center. La recopilación de bloqueos funciona tanto para aplicaciones beta como para aplicaciones activas, es decir, las enviadas a la App Store. Los registros de bloqueo contienen información valiosa para ayudarle a corregir el bloqueo.

Siga la sección Introducción si aún no ha configurado el SDK en la aplicación.

Además, los registros de bloqueo en macOS requieren la simbólica, consulte la documentación de Diagnósticos de App Center que explica cómo proporcionar símbolos para la aplicación.

Nota

En la 4.0.0 versión de App Center se introdujeron cambios importantes. Siga la sección Migrar a App Center SDK 4.0.0 y versiones posteriores para migrar App Center desde versiones anteriores.

Informes de bloqueos en extensiones

App Center admite informes de bloqueos en extensiones de macOS. El uso es el mismo que en la aplicación contenedora.

Generación de un bloqueo de prueba

App Center Crashes proporciona una API para generar un bloqueo de prueba para facilitar las pruebas del SDK. Esta API solo se puede usar en aplicaciones de prueba o beta y no hará nada en las aplicaciones de producción.

[MSACCrashes generateTestCrash];
Crashes.generateTestCrash()

Obtener más información sobre un bloqueo anterior

App Center Crashes tiene dos API que proporcionan más información en caso de que la aplicación se bloquee.

¿La aplicación recibió una advertencia de memoria baja en la sesión anterior?

En cualquier momento después de iniciar el SDK, puede comprobar si la aplicación recibió una advertencia de memoria en la sesión anterior:

[MSACCrashes hasReceivedMemoryWarningInLastSession];
Crashes.hasReceivedMemoryWarningInLastSession

Nota

Este método solo se debe usar después Crashes de que se haya iniciado, siempre devolverá NO o false antes del inicio.

Nota

En algunos casos, un dispositivo con poca memoria no puede enviar eventos.

¿Se bloqueó la aplicación en la sesión anterior?

En cualquier momento después de iniciar el SDK, puede comprobar si la aplicación se bloqueó en el inicio anterior:

[MSACCrashes hasCrashedInLastSession];
Crashes.hasCrashedInLastSession

Esto resulta útil en caso de que quieras ajustar el comportamiento o la interfaz de usuario de la aplicación después de que se haya producido un bloqueo.

Nota

Este método solo se debe usar después MSACCrashes de que se haya iniciado, siempre devolverá NO o false antes del inicio.

Detalles sobre el último bloqueo

Si la aplicación se bloqueó anteriormente, puedes obtener detalles sobre el último bloqueo.

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

Nota

Este método solo se debe usar después Crashes de que se haya iniciado, siempre devolverá nil antes del inicio.

Hay numerosos casos de uso para esta API, el más común es la gente que llama a esta API e implementa sus crashesDelegate personalizados.

Personalización del uso de bloqueos de App Center

App Center Crashes proporciona devoluciones de llamada para que los desarrolladores realicen acciones adicionales antes y al enviar registros de bloqueo a App Center.

Para agregar el comportamiento personalizado, debe adoptar el CrashesDelegateprotocolo -, todos sus métodos son opcionales.

Registro como delegado

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

Nota

Debe establecer el delegado antes de llamar a AppCenter.start, ya que App Center inicia el procesamiento se bloquea inmediatamente después del inicio.

¿Se debe procesar el bloqueo?

Implemente el crashes:shouldProcessErrorReport:-method en la clase que adopta el CrashesDelegate-protocol si desea decidir si es necesario procesar o no un bloqueo determinado. Por ejemplo, podría haber un bloqueo de nivel de sistema que le gustaría omitir y que no desea enviar a 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.
}

Errores controlados

App Center también permite realizar un seguimiento de los errores mediante excepciones controladas a través del trackError método . Opcionalmente, una aplicación puede adjuntar propiedades o datos adjuntos a un informe de errores controlado para proporcionar más contexto.

@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)
}

Para realizar un seguimiento de las excepciones, puede usar trackException el método :

@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)
}

Si la privacidad del usuario es importante para usted, es posible que quiera recibir la confirmación del usuario antes de enviar un informe de bloqueo a App Center. El SDK expone una devolución de llamada que indica a App Center Crashes que espere la confirmación del usuario antes de enviar los informes de bloqueo.

Si decide hacerlo, es responsable de obtener la confirmación del usuario, por ejemplo, a través de un mensaje de diálogo con una de las siguientes opciones: Enviar siempre, Enviar y No enviar. En función de la entrada, indicará a App Center qué hacer y, a continuación, se controlará el bloqueo en consecuencia.

Nota

El SDK no muestra un cuadro de diálogo para esto, la aplicación debe proporcionar su propia interfaz de usuario para solicitar el consentimiento del usuario.

El método siguiente muestra cómo configurar un controlador de confirmación de usuario:

[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.
})

En caso de que se devuelva YES/true en el bloque de controlador anterior, la aplicación debe obtener el permiso de usuario y enviar un mensaje al SDK con el resultado mediante la SIGUIENTE API. Si usa una alerta para esto, como hacemos en el ejemplo anterior, lo llamaría en función del resultado (ModalResponse) de runModal la llamada.

// 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)

Habilitación de la detección de excepciones no detectadas iniciadas en el subproceso principal

AppKit detecta excepciones iniciadas en el subproceso principal, lo que impide que la aplicación se bloquee en macOS, por lo que el SDK no puede detectar estos bloqueos. Para imitar el comportamiento de iOS, establezca la NSApplicationCrashOnExceptions marca antes de la inicialización del SDK, esta marca permite que la aplicación se bloquee en excepciones no detectadas y el SDK puede notificarlas.

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

Nota

El SDK de App Center establece la marca automáticamente en las versiones 1.10.0 y posteriores. A partir de la versión 1.11.0, esta marca ya no se establece automáticamente.

Deshabilitar el reenvío de las llamadas a métodos de la clase principal de la aplicación a Bloqueos de App Center

El SDK de Bloqueos de App Center usa swizzling para mejorar su integración reenviando a sí mismo algunas de las llamadas a métodos de la clase principal de la aplicación. El giro del método es una manera de cambiar la implementación de métodos en tiempo de ejecución. Si por alguna razón no desea usar el desenfocado (por ejemplo, debido a una directiva específica), debe invalidar los métodos y sendEvent: de reportException: la aplicación para que Bloqueos notifiquen las excepciones producidas en el subproceso principal correctamente.

  1. Cree el archivo ReportExceptionApplication.m y agregue la siguiente implementación:

    @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
    

    Nota

    trycatch/Swift no funciona con NSException. Estas excepciones solo se pueden controlar en Objective-C.

  2. Abra el archivo Info.plist y reemplace NSApplication en el campo Clase principal por el nombre de la clase de aplicación ReportExceptionApplication en este ejemplo.

  3. Para deshabilitar el giro en el SDK de App Center, agregue la AppCenterApplicationForwarderEnabled clave al archivo Info.plist y establezca el valor 0en .

Obtener información sobre el estado de envío de un registro de bloqueo

En ocasiones, quieres conocer el estado del bloqueo de la aplicación. Un caso de uso común es que es posible que quieras mostrar la interfaz de usuario que indica a los usuarios que la aplicación envía un informe de bloqueo o, en caso de que la aplicación se bloquee rápidamente después del inicio, quieres ajustar el comportamiento de la aplicación para asegurarte de que se pueden enviar los registros de bloqueo. El CrashesDelegateprotocolo -define tres devoluciones de llamada diferentes que puedes usar en la aplicación para recibir una notificación de lo que sucede:

La siguiente devolución de llamada se invocará antes de que el SDK envíe un registro de bloqueos.

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

En caso de que tengamos problemas de red o una interrupción en el punto de conexión y reinicie la aplicación, willSendErrorReport se desencadenará de nuevo después del reinicio del proceso.

La siguiente devolución de llamada se invocará después de que el SDK haya enviado correctamente un registro de bloqueo.

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

Se invocará la siguiente devolución de llamada si el SDK no pudo enviar un registro de bloqueos.

- (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 Recibir significa que se ha producido un error no recuperable, como un código 4xx. Por ejemplo, 401 significa que es appSecret incorrecto.

Esta devolución de llamada no se desencadena si se trata de un problema de red. En este caso, el SDK mantiene el reintento (y también detiene los reintentos mientras la conexión de red está inactiva).

Agregar datos adjuntos a un informe de bloqueo

Puede agregar datos adjuntos binarios y de texto a un informe de bloqueo. El SDK los enviará junto con el bloqueo para que pueda verlos en el portal de App Center. La siguiente devolución de llamada se invocará justo antes de enviar el bloqueo almacenado desde los inicios anteriores de la aplicación. No se invocará cuando se produzca el bloqueo. Este es un ejemplo de cómo adjuntar texto y una imagen a un bloqueo:

- (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!]
}

Nota

El límite de tamaño es actualmente de 7 MB. Si intenta enviar datos adjuntos más grandes, se producirá un error.

Habilitar o deshabilitar bloqueos de App Center en tiempo de ejecución

Puede habilitar y deshabilitar bloqueos de App Center en tiempo de ejecución. Si la deshabilita, el SDK no hará ningún informe de bloqueos para la aplicación.

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

Para habilitar app Center Crashes de nuevo, use la misma API, pero pase YES/true como parámetro.

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

El estado se conserva en el almacenamiento del dispositivo en los inicios de la aplicación.

Nota

Este método solo se debe usar después Crashes de que se haya iniciado.

Comprobación de si los bloqueos de App Center están habilitados

También puede comprobar si Los bloqueos de App Center están habilitados o no:

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

Nota

Este método solo se debe usar después Crashes de que se haya iniciado, siempre false devolverá antes de comenzar.

Deshabilitación del control de excepciones de Mach

De forma predeterminada, App Center Crashes usa el controlador de excepciones Mach para detectar señales fatales, por ejemplo, desbordamientos de pila a través de un servidor de excepciones Mach.

El disableMachExceptionHandlermétodo -proporciona una opción para deshabilitar la detección de señales fatales a través de un servidor de excepciones Mach. Si desea deshabilitar el controlador de excepciones Mach, debe llamar a este método ANTES de iniciar el SDK. El código de instalación típico tendría este aspecto:

[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])