Firebase is back at Google I/O on May 10! Register now

Настройте отчеты о сбоях Firebase Crashlytics

Оптимизируйте свои подборки Сохраняйте и классифицируйте контент в соответствии со своими настройками.

В этом руководстве описывается, как настроить отчеты о сбоях с помощью Firebase Crashlytics SDK. По умолчанию Crashlytics автоматически собирает отчеты о сбоях для всех пользователей вашего приложения (вы можете отключить автоматические отчеты о сбоях и вместо этого включить отчеты по подписке для своих пользователей). Crashlytics предоставляет четыре механизма ведения журнала «из коробки»: настраиваемые ключи , настраиваемые журналы , идентификаторы пользователей и перехваченные исключения .

Добавить пользовательские ключи

Пользовательские ключи помогают вам получить конкретное состояние вашего приложения, ведущее к сбою. Вы можете связать произвольные пары ключ/значение с отчетами о сбоях, а затем использовать настраиваемые ключи для поиска и фильтрации отчетов о сбоях в консоли Firebase.

  • На панели инструментов Crashlytics вы можете искать задачи, соответствующие пользовательскому ключу.
  • Когда вы просматриваете конкретную проблему в консоли, вы можете просматривать связанные пользовательские ключи для каждого события (подвкладка «Ключи ») и даже фильтровать события по пользовательским ключам (меню «Фильтр» в верхней части страницы).

Используйте метод setCustomValue для установки пар ключ/значение. Например:

Быстрый

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

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

Цель-C

При установке целых чисел, логических значений или чисел с плавающей запятой поместите значение как @( 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"];

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

Быстрый

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

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

Цель-C

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

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

Массовое добавление пар ключ/значение с помощью метода setCustomKeysAndValues ​​с NSDictionary в качестве единственного параметра:

Быстрый

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)

Цель-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];

Добавить пользовательские сообщения журнала

Чтобы получить больше информации о событиях, приведших к сбою, вы можете добавить в свое приложение пользовательские журналы Crashlytics. Crashlytics связывает журналы с данными о сбоях и отображает их на странице Crashlytics консоли Firebase на вкладке «Журналы» .

Быстрый

Используйте log() или log(format:, arguments:) , чтобы выявить проблемы. Если вы хотите получить полезные выходные данные журнала с сообщениями, объект, который вы передаете в log() , должен соответствовать свойству CustomStringConvertible . log() возвращает свойство описания, которое вы определили для объекта. Например:

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

.log(format:, arguments:) форматирует значения, возвращаемые при вызове getVaList() . Например:

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

Дополнительные сведения об использовании log() или log(format:, arguments:) см. в справочной документации Crashlytics.

Цель-C

Используйте log или logWithFormat , чтобы точно определить проблемы. Обратите внимание: если вы хотите получить полезные выходные данные журнала с сообщениями, объект, который вы передаете любому методу, должен переопределить свойство экземпляра description . Например:

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

Дополнительные сведения об использовании log и logWithFormat см. в справочной документации по Crashlytics.

Установить идентификаторы пользователей

Чтобы диагностировать проблему, часто полезно знать, кто из ваших пользователей столкнулся с данным сбоем. Crashlytics включает способ анонимной идентификации пользователей в ваших отчетах о сбоях.

Чтобы добавить идентификаторы пользователей в отчеты, назначьте каждому пользователю уникальный идентификатор в виде идентификационного номера, токена или хешированного значения:

Быстрый

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

Цель-C

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

Если вам когда-нибудь понадобится очистить идентификатор пользователя после его установки, сбросьте значение до пустой строки. Очистка идентификатора пользователя не удаляет существующие записи Crashlytics. Если вам нужно удалить записи, связанные с идентификатором пользователя, обратитесь в службу поддержки Firebase .

Сообщить о нефатальных исключениях

В дополнение к автоматическим сообщениям о сбоях вашего приложения, Crashlytics позволяет записывать нефатальные исключения и отправлять их вам при следующем запуске вашего приложения.

Вы можете записывать нефатальные исключения, записывая объекты NSError с помощью метода recordError . recordError фиксирует стек вызовов потока, вызывая [NSThread callStackReturnAddresses] .

Быстрый

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

Цель-C

[[FIRCrashlytics crashlytics] recordError:error];

При использовании метода recordError важно понимать структуру NSError и то, как Crashlytics использует данные для группировки сбоев. Неправильное использование метода recordError может привести к непредсказуемому поведению и может привести к тому, что Crashlytics ограничит отчеты о зарегистрированных ошибках для вашего приложения.

Объект NSError имеет три аргумента:

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

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

Быстрый

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)

Цель-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];

Когда вы регистрируете указанную выше ошибку, создается новая проблема, сгруппированная по NSSomeErrorDomain и -1001 . Дополнительные зарегистрированные ошибки, использующие один и тот же домен и значения кода, сгруппированы в одной проблеме. Данные, содержащиеся в объекте userInfo , преобразуются в пары ключ-значение и отображаются в разделе ключей/журналов в рамках отдельной задачи.

Журналы и пользовательские ключи

Как и в отчетах о сбоях, вы можете встраивать журналы и пользовательские ключи, чтобы добавить контекст в NSError . Однако есть разница в том, какие журналы прикрепляются к сбоям, а какие к регистрируемым ошибкам. Когда происходит сбой и приложение перезапускается, журналы, которые Crashlytics извлекает с диска, — это те, которые были записаны непосредственно до момента сбоя. Когда вы регистрируете NSError , приложение не завершается немедленно. Поскольку Crashlytics отправляет зарегистрированный отчет об ошибке только при следующем запуске приложения и должен ограничивать объем пространства, отведенного для журналов на диске, после записи NSError можно вести достаточно журналов, чтобы все соответствующие журналы были удалены к моменту отправки Crashlytics. отчет с устройства. Помните об этом балансе при регистрации NSErrors и использовании журналов и пользовательских ключей в своем приложении.

Соображения производительности

Имейте в виду, что регистрация NSError может быть довольно дорогой. Во время выполнения вызова Crashlytics захватывает стек вызовов текущего потока, используя процесс, называемый раскручиванием стека. Этот процесс может сильно нагружать ЦП и ввод-вывод, особенно на архитектурах, поддерживающих раскрутку DWARF (arm64 и x86). После завершения раскрутки информация синхронно записывается на диск. Это предотвращает потерю данных в случае сбоя следующей строки.

Хотя безопасно вызывать этот API в фоновом потоке, помните, что при отправке этого вызова в другую очередь теряется контекст текущей трассировки стека.

Как насчет NSExceptions?

Crashlytics не предлагает средства для ведения журнала и записи экземпляров NSException напрямую. Вообще говоря, API Cocoa и Cocoa Touch не защищены от исключений. Это означает, что использование @catch может иметь очень серьезные непреднамеренные побочные эффекты в вашем процессе, даже если использовать его с особой осторожностью. Вы никогда не должны использовать операторы @catch в своем коде. Пожалуйста, обратитесь к документации Apple по этой теме.

Настройка трассировки стека

Если ваше приложение работает в неродной среде (такой как C++ или Unity), вы можете использовать API модели исключений, чтобы сообщать метаданные о сбоях в собственном формате исключений вашего приложения. Сообщенные исключения помечены как нефатальные.

Быстрый

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)

Цель-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];

Пользовательские кадры стека также могут быть инициализированы только адресами:

Быстрый

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)

Цель-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];

Включить отчетность по подписке

По умолчанию Crashlytics автоматически собирает отчеты о сбоях для всех пользователей вашего приложения. Чтобы предоставить пользователям больший контроль над отправляемыми ими данными, вы можете включить отчеты по подписке, отключив автоматические отчеты и отправляя данные в Crashlytics только тогда, когда вы решите это в своем коде:

  1. Отключите автоматический сбор, добавив новый ключ в файл Info.plist :

    • Ключ: FirebaseCrashlyticsCollectionEnabled
    • Значение: false
  2. Включите сбор для избранных пользователей, вызвав переопределение сбора данных Crashlytics во время выполнения. Значение переопределения сохраняется при запуске вашего приложения, поэтому Crashlytics может автоматически собирать отчеты.

    Чтобы отказаться от автоматических отчетов о сбоях, передайте false в качестве значения переопределения. Если задано значение false , новое значение не применяется до следующего запуска приложения.

    Быстрый

    Crashlytics.crashlytics().setCrashlyticsCollectionEnabled(true)

    Цель-C

    [[FIRCrashlytics crashlytics] setCrashlyticsCollectionEnabled:YES];

Управление данными Crash Insights

Crash Insights помогает решать проблемы, сравнивая ваши анонимные трассировки стека с трассировками из других приложений Firebase и сообщая, является ли ваша проблема частью более широкой тенденции. Для многих проблем Crash Insights даже предоставляет ресурсы, которые помогут вам отладить сбой.

Crash Insights использует агрегированные данные о сбоях для определения общих тенденций стабильности. Если вы предпочитаете не делиться данными своего приложения, вы можете отказаться от Crash Insights в меню Crash Insights в верхней части списка проблем Crashlytics в консоли Firebase .