Personalizar relatórios de erros do Firebase Crashlytics


No painel do Crashlytics, clique em um problema e acesse um relatório de eventos detalhado. É possível personalizar esses relatórios para entender melhor o que está acontecendo no app e as circunstâncias em torno dos eventos informados ao Crashlytics.

  • Receba automaticamente registros de navegação estrutural se o app usar o SDK do Firebase para Google Analytics. Com eles, é possível visualizar as ações do usuário antes de um evento coletado pelo Crashlytics no seu app.

  • Desative a geração automática de relatórios de falhas e ative a permissão para geração de relatórios para seus usuários. Por padrão, o Crashlytics coleta automaticamente relatórios de erros para todos os usuários do seu app.

Adicionar chaves personalizadas

As chaves personalizadas ajudam você a saber o estado específico do seu app antes de 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 (menu de Filtro na parte de cima da página).

Use o método setCustomValue para definir pares de chave-valor. Por 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. Por 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 contextualizar melhor os eventos antes de 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 no 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ê transmitir para log() precisará estar em conformidade com a propriedade CustomStringConvertible. log() retorna a propriedade de descrição definida para o objeto. Por exemplo:

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

.log(format:, arguments:) formata valores retornados da chamada getVaList(). Por 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. Por 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, é útil identificar os usuários que 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"];

Para 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 vigentes do Crashlytics. Para excluir registros associados a um ID do usuário, entre em contato com o suporte do Firebase.

Relatar exceções não fatais

Além de relatar 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. Por 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 aqueles 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 após a gravação de um NSError 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 aceitam 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. Ou seja, a utilização de @catch pode ter efeitos indesejados muito graves no seu processo, mesmo quando é feita com muito cuidado. Nunca use instruções @catch no seu código. Consulte a documentação da Apple 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];

Acessar registros de navegação estrutural

Os registros de navegação estrutural oferecem uma melhor compreensão das interações de um usuário com seu app que levaram a um evento não fatal, de falha ou de ANR. Esses registros podem ser úteis ao tentar reproduzir e depurar um problema.

Os registros de navegação estrutural usam a tecnologia do Google Analytics. Para receber esses registros, ative o Google Analytics no seu projeto do Firebase e adicione o SDK do Firebase para Google Analytics ao seu app. Quando esses requisitos forem atendidos, os registros de navegação estrutural serão incluídos automaticamente com os dados de um evento na guia Registros quando você visualizar os detalhes de um problema.

O SDK do Analytics registra automaticamente o evento screen_view, o que permite que os registros de navegação estrutural mostrem uma lista de telas visualizadas antes do evento não fatal, de falha ou de ANR. Um registro de navegação estrutural screen_view contém um parâmetro firebase_screen_class.

Os registros de navegação estrutural também são preenchidos com todos os eventos personalizados registrados manualmente na sessão do usuário, incluindo os dados de parâmetro do evento. Esses dados podem ajudar a mostrar uma série de ações do usuário antes de um evento não fatal, de falha ou de ANR.

É possível controlar a coleta e o uso de dados do Google Analytics, o que inclui os dados que preenchem os registros de navegação estrutural.

Ativar 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. Para dar aos usuários mais controle sobre os dados que são enviados, ative a permissão para geração de relatórios. Para fazer isso, basta desativar a criação automática de relatórios e só enviar dados ao Crashlytics quando você quiser 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 dos usuários selecionados, chame a modificação da coleta de dados do Crashlytics no ambiente de execução. O valor de substituição persiste em diferentes lançamentos do seu app. Dessa forma, o Crashlytics consegue coletar relatórios automaticamente.

    Para não participar da geração automática de relatórios 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.