Personalizar relatórios de erros do Firebase Crashlytics

Veja neste guia como personalizar seus relatórios de erros usando o SDK do Firebase Crashlytics. Por padrão, o Crashlytics coleta automaticamente relatórios de erros para todos os usuários do seu app. Se quiser, desative os relatórios de falhas automáticos e ative os relatórios de permissão para os usuários. O Crashlytics oferece quatro mecanismos de geração de registros prontos para uso: chaves personalizadas, registros personalizados, identificadores de usuários e exceções capturadas.

Adicionar chaves personalizadas

As chaves personalizadas ajudam você a chegar ao estado específico do seu aplicativo que está gerando uma falha. É possível associar pares de chave-valor arbitrários aos seus relatórios de erros e usar as chaves personalizadas para pesquisar e filtrar relatórios de erros no Console do Firebase.

  • No painel do Crashlytics, é possível pesquisar problemas que correspondam a uma chave personalizada.
  • Ao analisar um problema específico no console, você pode ver as chaves personalizadas associadas a cada evento (subguia Chaves) e até mesmo filtrar os eventos por chaves personalizadas (Filtro, na parte de cima da página).

Use o método setCustomValue para definir pares de chave-valor. Exemplo:

Swift

// Set int_key to 100.
Crashlytics.crashlytics().setCustomValue(100, forKey: "int_key")

// Set str_key to "hello".
Crashlytics.crashlytics().setCustomValue("hello", forKey: "str_key")

Objective-C

Ao definir números inteiros, booleanos ou flutuantes, defina o valor como @(value).

// Set int_key to 100.
[[FIRCrashlytics crashlytics] setCustomValue:@(100) forKey:@"int_key"];

// Set str_key to "hello".
[[FIRCrashlytics crashlytics] setCustomValue:@"hello" forKey:@"str_key"];

Para modificar o valor de uma chave atual, chame a chave e defina-a com um valor diferente. Exemplo:

Swift

Crashlytics.crashlytics().setCustomValue(100, forKey: "int_key")

// Set int_key to 50 from 100.
Crashlytics.crashlytics().setCustomValue(50, forKey: "int_key")

Objective-C

[[FIRCrashlytics crashlytics] setCustomValue:@(100) forKey:@"int_key"];

// Set int_key to 50 from 100.
[[FIRCrashlytics crashlytics] setCustomValue:@(50) forKey:@"int_key"];

Adicione pares de chave-valor em massa usando o método setCustomKeysAndValues com um NSDictionary como o único parâmetro:

Swift

let keysAndValues = [
                 "string key" : "string value",
                 "string key 2" : "string value 2",
                 "boolean key" : true,
                 "boolean key 2" : false,
                 "float key" : 1.01,
                 "float key 2" : 2.02
                ] as [String : Any]

Crashlytics.crashlytics().setCustomKeysAndValues(keysAndValues)

Objective-C

NSDictionary *keysAndValues =
    @{@"string key" : @"string value",
      @"string key 2" : @"string value 2",
      @"boolean key" : @(YES),
      @"boolean key 2" : @(NO),
      @"float key" : @(1.01),
      @"float key 2" : @(2.02)};

[[FIRCrashlytics crashlytics] setCustomKeysAndValues: keysAndValues];

Adicionar mensagens de registro personalizadas

Para dar mais contexto aos eventos que geram uma falha, adicione registros personalizados do Crashlytics ao seu app. O Crashlytics associa os registros aos dados de falhas e os exibe na página do Crashlytics do Console do Firebase, na guia Registros.

Swift

Use log() ou log(format:, arguments:) para ajudar a identificar problemas. Se você quiser receber uma saída de registro útil com mensagens, o objeto que você passar para log() precisará estar em conformidade com a propriedade CustomStringConvertible. log() retorna a propriedade de descrição definida para o objeto. Exemplo:

Crashlytics.crashlytics().log("Higgs-Boson detected! Bailing out…, \(attributesDict)")

.log(format:, arguments:) formata valores retornados da chamada getVaList(). Exemplo:

Crashlytics.crashlytics().log(format: "%@, %@", arguments: getVaList(["Higgs-Boson detected! Bailing out…", attributesDict]))

Para mais detalhes sobre como usar log() ou log(format:, arguments:), consulte a documentação de referência do Crashlytics.

Objective-C

Use log ou logWithFormat para ajudar a identificar problemas. Se você quiser receber uma saída de registro útil com mensagens, o objeto que você passar para qualquer método precisará modificar a propriedade da instância description. Exemplo:

[[FIRCrashlytics crashlytics] log:@"Simple string message"];

[[FIRCrashlytics crashlytics] logWithFormat:@"Higgs-Boson detected! Bailing out... %@", attributesDict];

[[FIRCrashlytics crashlytics] logWithFormat:@"Logging a variable argument list %@" arguments:va_list_arg];

Para mais detalhes sobre como usar log e logWithFormat, consulte a documentação de referência do Crashlytics.

Definir identificadores de usuários

Para diagnosticar um problema, muitas vezes é útil saber quais usuários observaram uma determinada falha. O Crashlytics inclui uma maneira de identificar anonimamente os usuários nos relatórios de erros.

Para adicionar IDs de usuários aos seus relatórios, atribua a cada usuário um identificador exclusivo na forma de um número de ID, token ou valor de hash:

Swift

Crashlytics.crashlytics().setUserID("123456789")

Objective-C

[[FIRCrashlytics crashlytics] setUserID:@"123456789"];

Se você precisar apagar um identificador de usuário depois de configurá-lo, redefina o valor deixando uma string em branco. Limpar um identificador de usuário não remove os registros atuais do Crashlytics. Se você precisar excluir registros associados a um ID de usuário, entre em contato com o suporte do Firebase.

Reportar exceções não fatais

Além de reportar automaticamente as falhas do seu app, o Crashlytics permite registrar exceções não fatais e enviá-las para você na próxima vez que o app for iniciado.

Para registrar exceções não fatais, grave objetos NSError com o método recordError. recordError captura a pilha de chamadas da linha de execução ao chamar [NSThread callStackReturnAddresses].

Swift

Crashlytics.crashlytics().record(error: error)

Objective-C

[[FIRCrashlytics crashlytics] recordError:error];

Ao usar o método recordError, é importante entender a estrutura NSError e como o Crashlytics usa os dados para agrupar falhas. O uso incorreto do método recordError pode causar um comportamento imprevisível e fazer com que o Crashlytics limite os relatórios de erros registrados para seu app.

Um objeto NSError tem três argumentos:

  • domain: String
  • code: Int
  • userInfo: [AnyHashable : Any]? = nil

Ao contrário das falhas fatais, que são agrupadas usando a análise de stack trace, os erros registrados são agrupados por domain e code. Esta é uma distinção importante entre falhas fatais e erros registrados. Exemplo:

Swift

let userInfo = [
  NSLocalizedDescriptionKey: NSLocalizedString("The request failed.", comment: ""),
  NSLocalizedFailureReasonErrorKey: NSLocalizedString("The response returned a 404.", comment: ""),
  NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString("Does this page exist?", comment: ""),
  "ProductID": "123456",
  "View": "MainView"
]

let error = NSError.init(domain: NSCocoaErrorDomain,
                         code: -1001,
                         userInfo: userInfo)

Objective-C

NSDictionary *userInfo = @{
  NSLocalizedDescriptionKey: NSLocalizedString(@"The request failed.", nil),
  NSLocalizedFailureReasonErrorKey: NSLocalizedString(@"The response returned a 404.", nil),
  NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(@"Does this page exist?", nil),
  @"ProductID": @"123456",
  @"View": @"MainView",
};

NSError *error = [NSError errorWithDomain:NSCocoaErrorDomain
                                     code:-1001
                                 userInfo:userInfo];

Quando você registra o erro acima, ele cria um novo problema agrupado por NSSomeErrorDomain e -1001. Outros erros registrados que usam os mesmos valores de domínio e código são agrupados no mesmo problema. Os dados contidos no objeto userInfo são convertidos em pares de chave-valor e exibidos na seção de chaves/registros em um problema individual.

Registros e chaves personalizadas

Assim como os relatórios de erros, é possível incorporar registros e chaves personalizadas para dar mais contexto ao NSError. No entanto, há uma diferença entre registros anexados a falhas e erros registrados. Quando ocorre uma falha e o app é reiniciado, os registros do Crashlytics recuperados do disco são os que foram gravados até o momento da falha. Quando você registra um NSError, o app não é encerrado imediatamente. Como o Crashlytics só envia o relatório de erros registrado na próxima inicialização do app e precisa limitar a quantidade de espaço alocado para os registros no disco, é possível registrar o suficiente depois que um NSError é gravado para que todos os registros relevantes sejam substituídos no momento em que o Crashlytics enviar o relatório do dispositivo. Lembre-se desse equilíbrio ao registrar NSErrors e usar registros e chaves personalizadas no seu app.

Considerações sobre desempenho

Lembre-se de que registrar um NSError pode ser bastante caro. No momento em que você faz a chamada, o Crashlytics captura a pilha de chamadas da linha de execução atual usando um processo chamado desenrolamento de pilha. Esse processo pode consumir muita CPU e E/S, principalmente em arquiteturas que suportam o desenrolamento DWARF (arm64 e x86). Quando o desenrolamento estiver finalizado, as informações serão gravadas em disco de maneira síncrona. Isso evita a perda de dados se a próxima linha falhar.

Embora seja seguro chamar essa API em uma linha de execução em segundo plano, lembre-se de que o envio dessa chamada para outra fila perde o contexto do rastreamento de pilha atual.

E as NSExceptions?

O Crashlytics não oferece um recurso para registrar e gravar instâncias NSException diretamente. De modo geral, as APIs Cocoa e Cocoa Touch não são seguras contra exceções. Isso significa que o uso de @catch pode ter efeitos indesejados muito graves no seu processo, mesmo quando usado com muito cuidado. Nunca use instruções @catch no seu código. Consulte a documentação da Apple (em inglês) sobre o assunto.

Personalizar stack traces

Se o app for executado em um ambiente não nativo, como C ++ ou Unity, será possível usar a API Exception Model para reportar metadados de falha no formato de exceção nativa do app. As exceções reportadas são consideradas não fatais.

Swift

var  ex = ExceptionModel(name:"FooException", reason:"There was a foo.")
ex.stackTrace = [
  StackFrame(symbol:"makeError", file:"handler.js", line:495),
  StackFrame(symbol:"then", file:"routes.js", line:102),
  StackFrame(symbol:"main", file:"app.js", line:12),
]

crashlytics.record(exceptionModel:ex)

Objective-C

FIRExceptionModel *model =
    [FIRExceptionModel exceptionModelWithName:@"FooException" reason:@"There was a foo."];
model.stackTrace = @[
  [FIRStackFrame stackFrameWithSymbol:@"makeError" file:@"handler.js" line:495],
  [FIRStackFrame stackFrameWithSymbol:@"then" file:@"routes.js" line:102],
  [FIRStackFrame stackFrameWithSymbol:@"main" file:@"app.js" line:12],
];

[[FIRCrashlytics crashlytics] recordExceptionModel:model];

Frames de pilha personalizados também podem ser inicializados apenas com endereços:

Swift

var  ex = ExceptionModel.init(name:"FooException", reason:"There was a foo.")
ex.stackTrace = [
  StackFrame(address:0xfa12123),
  StackFrame(address:12412412),
  StackFrame(address:194129124),
]

crashlytics.record(exceptionModel:ex)

Objective-C

FIRExceptionModel *model =
    [FIRExceptionModel exceptionModelWithName:@"FooException" reason:@"There was a foo."];
model.stackTrace = @[
  [FIRStackFrame stackFrameWithAddress:0xfa12123],
  [FIRStackFrame stackFrameWithAddress:12412412],
  [FIRStackFrame stackFrameWithAddress:194129124],
];

[[FIRCrashlytics crashlytics] recordExceptionModel:model];

Ative a permissão para geração de relatórios

Por padrão, o Crashlytics coleta automaticamente relatórios de erros para todos os usuários do seu app. Caso queira dar aos usuários mais controle sobre os dados enviados, é possível ativar a geração de relatórios. Para fazer isso, basta desativar a geração automática de relatórios e enviar dados ao Crashlytics somente quando você escolher no seu código:

  1. Para desativar a coleta automática, adicione uma nova chave ao arquivo Info.plist:

    • Chave: FirebaseCrashlyticsCollectionEnabled
    • Valor: false
  2. Para ativar a coleta nos usuários selecionados, chame a modificação da coleta de dados do Crashlytics no ambiente de execução. O valor de modificação continua entre os lançamentos do seu app. Assim, o Crashlytics pode coletar relatórios automaticamente.

    Para desativar o relatório automático de falhas, transmita false como o valor de modificação. Quando definido como false, o novo valor não se aplica até a próxima execução do app.

    Swift

    Crashlytics.crashlytics().setCrashlyticsCollectionEnabled(true)

    Objective-C

    [[FIRCrashlytics crashlytics] setCrashlyticsCollectionEnabled:YES];

Gerenciar dados do Crash Insights

Com o Crash Insights, você soluciona problemas quando compara stack traces anônimos a traces de outros aplicativos do Firebase. Você receberá um aviso se o problema fizer parte de uma tendência maior. Para muitos problemas, o Crash Insights também oferece recursos para ajudar a depurar a falha.

Ele usa dados de falhas agregados para identificar tendências de estabilidade comuns. Caso prefira não compartilhar os dados do app, é possível desativar o Crash Insights no menu com o mesmo nome na parte de cima da lista de problemas do Crashlytics no Console do Firebase.