Partilhar via


Falhas do App Center (macOS)

Importante

O Visual Studio App Center foi desativado em 31 de março de 2025, exceto para os recursos de Análise e Diagnóstico, que continuarão a ter suporte até 30 de junho de 2026. Mais informações.

As falhas do App Center gerarão automaticamente um registro de falhas sempre que seu aplicativo falhar. O log é gravado primeiro no armazenamento do dispositivo e, quando o usuário iniciar o aplicativo novamente, o relatório de falhas será enviado para o App Center. A coleta de falhas funciona tanto para aplicativos beta quanto para aplicativos ao vivo, ou seja, aqueles enviados para a App Store. Os registos de falhas contêm informações valiosas para ajudar a corrigir a falha.

Siga a seção Introdução se ainda não configurou o SDK em seu aplicativo.

Além disso, os logs de falhas no macOS exigem Simbolização. Verifica a documentação de Diagnóstico do App Center que explica como fornecer símbolos para a sua aplicação.

Observação

Alterações significativas foram introduzidas na versão do 4.0.0 App Center. Siga a seção Migrar para o SDK do App Center 4.0.0 e superior para migrar o App Center de versões anteriores.

Relatório de falhas em extensões

O App Center suporta relatórios de falhas em extensões macOS. O uso é o mesmo que no aplicativo de contêiner.

Gerar uma falha de teste

O App Center Crashes fornece uma API para gerar uma falha de teste para facilitar o teste do SDK. Essa API só pode ser usada em aplicativos de teste/beta e não fará nada em aplicativos de produção.

[MSACCrashes generateTestCrash];
Crashes.generateTestCrash()

Obter mais informações sobre uma falha anterior

O App Center Crashes tem duas APIs que fornecem mais informações caso seu aplicativo tenha falhado.

O aplicativo recebeu um aviso de falta de memória na sessão anterior?

A qualquer momento depois de iniciar o SDK, você pode verificar se o aplicativo recebeu um aviso de memória na sessão anterior:

[MSACCrashes hasReceivedMemoryWarningInLastSession];
Crashes.hasReceivedMemoryWarningInLastSession

Observação

Este método só deve ser usado depois de Crashes ter sido iniciado, ele sempre retornará NO ou false antes de começar.

Observação

Em alguns casos, um dispositivo com pouca memória não pode enviar eventos.

O aplicativo travou na sessão anterior?

A qualquer momento depois de iniciar o SDK, você pode verificar se o aplicativo falhou na inicialização anterior:

[MSACCrashes hasCrashedInLastSession];
Crashes.hasCrashedInLastSession

Isso é útil caso você queira ajustar o comportamento ou a interface do usuário do seu aplicativo após a ocorrência de uma falha.

Observação

Este método só deve ser usado depois de MSACCrashes ter sido iniciado, ele sempre retornará NO ou false antes de começar.

Detalhes sobre o último acidente

Se o seu aplicativo travou anteriormente, você pode obter detalhes sobre a última falha.

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

Observação

Este método só deve ser usado depois de Crashes ter sido iniciado, ele sempre retornará nil antes de começar.

Existem inúmeros casos de uso para essa API, o mais comum são pessoas que chamam essa API e implementam seu CrashesDelegate personalizado.

Personalize o uso do App Center Crashes

O App Center Crashes fornece callbacks para que os desenvolvedores executem ações adicionais antes e quando enviam logs de falhas para o App Center.

Para adicionar seu comportamento personalizado, você precisa adotar o CrashesDelegate-protocol, todos os seus métodos são opcionais.

Registe-se como delegado

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

Observação

Você deve definir o delegado antes de chamar AppCenter.start, já que o App Center começa a processar falhas imediatamente após o início.

O acidente deve ser processado?

Implemente o método crashes:shouldProcessErrorReport: na classe que adota o protocolo CrashesDelegate se quiser decidir se uma falha específica precisa ser processada ou não. Por exemplo, pode haver uma falha no nível do sistema que você gostaria de ignorar e que não deseja enviar para o 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.
}

Erros tratados

O App Center também permite que você rastreie erros usando exceções manipuladas via trackError método. Opcionalmente, um aplicativo pode anexar propriedades e/ou anexos a um relatório de erros manipulado para fornecer mais 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 exceções de rastreamento, você pode usar o método 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)
}

Se a privacidade do usuário for importante para você, convém obter a confirmação do usuário antes de enviar um relatório de falhas para o App Center. O SDK expõe um callback que indica ao App Center Crashes para aguardar a confirmação do utilizador antes de enviar qualquer relatório de falhas.

Se você optar por fazê-lo, você é responsável por obter a confirmação do usuário, por exemplo, através de um prompt de diálogo com uma das seguintes opções: Sempre enviar, Enviar e Não enviar. Com base na entrada, você informará ao App Center Crashes o que fazer e a falha será tratada de acordo.

Observação

O SDK não exibe uma caixa de diálogo para isso, o aplicativo deve fornecer sua própria interface do usuário para solicitar o consentimento do usuário.

O método a seguir mostra como configurar um manipulador de confirmação do usuário:

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

Caso você retorne YES/true no bloco de manipulador acima, seu aplicativo deve obter permissão de usuário e enviar uma mensagem ao SDK com o resultado usando a seguinte API. Se você estiver usando um alerta para isso, como fazemos no exemplo acima, você o chamaria com base no resultado (ModalResponse) da runModal chamada.

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

Habilitar a captura de exceções não detetadas lançadas no thread principal

O AppKit captura exceções lançadas no thread principal, impedindo que o aplicativo falhe no macOS, portanto, o SDK não pode capturar essas falhas. Para imitar o comportamento do iOS, defina NSApplicationCrashOnExceptions o sinalizador antes da inicialização do SDK, esse sinalizador permite que o aplicativo falhe em exceções não detetadas e o SDK pode relatá-las.

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

Observação

O SDK do App Center define o sinalizador automaticamente nas versões 1.10.0 e inferiores. A partir da versão 1.11.0, esse sinalizador não é mais definido automaticamente.

Desativar o encaminhamento de chamadas de métodos da classe principal do aplicativo para o App Center Falhas

O SDK do App Center Crashes usa swizzling para melhorar a sua integração, encaminhando para si mesmo algumas das chamadas de métodos da classe principal da aplicação. A rotação de métodos é uma maneira de alterar a implementação de métodos em tempo de execução. Se, por qualquer motivo, não pretender utilizar o swizzling (por exemplo, devido a uma política específica), deve sobrescrever os métodos reportException: e sendEvent: da aplicação por si mesmo, para que o Crashes relate corretamente as exceções lançadas no thread principal.

  1. Crie o arquivo ReportExceptionApplication.m e adicione a seguinte implementação:

    @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
    

    Observação

    Swift try/catch não funciona com NSException. Essas exceções podem ser tratadas apenas em Objective-C.

  2. Abra o arquivo Info.plist e substitua o NSApplication no campo Principal class pelo nome da classe do aplicativo, ReportExceptionApplication neste exemplo.

  3. Para desativar o swizzling no SDK do App Center, adicione a chave AppCenterApplicationForwarderEnabled ao ficheiro Info.plist e defina o valor para 0.

Obter informações sobre o estado de envio de um registo de falhas

Às vezes, você quer saber o status da falha do seu aplicativo. Um caso de uso comum é que você pode querer mostrar a interface do usuário que informa aos usuários que seu aplicativo está enviando um relatório de falhas ou, caso seu aplicativo esteja falhando rapidamente após a inicialização, você deseja ajustar o comportamento do aplicativo para garantir que os logs de falhas possam ser enviados. O CrashesDelegate-protocol define três retornos de chamada diferentes que você pode usar em seu aplicativo para ser notificado do que está acontecendo:

O seguinte callback será invocado antes que o SDK envie um registo de falhas

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

No caso de termos problemas de rede ou uma interrupção no ponto de extremidade, e você reiniciar o aplicativo, willSendErrorReport é acionado novamente após a reinicialização do processo.

A função de retorno seguinte será invocada depois do SDK enviar com sucesso um relatório de falhas.

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

O seguinte callback será invocado se o SDK não conseguir enviar um registo de falhas.

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

Receber didFailSendingErrorReport significa que ocorreu um erro não recuperável, como ocorreu um código 4xx . Por exemplo, 401 significa que o appSecret está errado.

Esse retorno de chamada não é acionado se for um problema de rede. Nesse caso, o SDK continua tentando novamente (e também pausa novas tentativas enquanto a conexão de rede está inativa).

Adicionar anexos a um relatório de falhas

Você pode adicionar anexos binários e de texto a um relatório de falhas. O SDK irá enviá-los juntamente com o crash para que os possa ver no portal do App Center. O seguinte callback será invocado imediatamente antes de enviar a falha armazenada de lançamentos anteriores do aplicativo. Ele não será invocado quando a falha acontecer. Aqui está um exemplo de como anexar texto e uma imagem a uma falha:

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

Observação

Atualmente, o limite de tamanho é de 7 MB. A tentativa de enviar um anexo maior desencadeará um erro.

Ativar ou desativar falhas do App Center em tempo de execução

Você pode habilitar e desabilitar a funcionalidade Crashes do App Center em tempo de execução. Se você desativá-lo, o SDK não fará nenhum relatório de falhas para o aplicativo.

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

Para reativar o Crashes do App Center, utilize a mesma API, mas passe YES/true como parâmetro.

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

O estado é persistido no armazenamento do dispositivo em todas as inicializações de aplicativos.

Observação

Este método só deve ser utilizado depois de Crashes ter sido iniciado.

Verifique se o App Center Crashes está ativado

Você também pode verificar se o App Center Crashes está ativado ou não:

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

Observação

Este método só deve ser usado depois de Crashes ter sido iniciado, ele sempre retornará false antes de começar.

Desativando o tratamento de exceções Mach

Por padrão, o App Center Crashes usa o manipulador de exceções Mach para capturar sinais fatais, por exemplo, estouros de pilha, por meio de um servidor de exceção Mach.

O disableMachExceptionHandler-method fornece uma opção para desativar a captura de sinais fatais através de um servidor de exceção Mach. Se você quiser desabilitar o manipulador de exceção Mach, você deve chamar esse método ANTES de iniciar o SDK. Seu código de configuração típico ficaria assim:

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