[Flutter - Firebase] Firebase Crashlytics

jaehee kim·2021년 9월 9일
2

Flutter - Firebase

목록 보기
2/2
post-thumbnail

Firebase Crashlytics

Firebase Crashlytics에 대해서 알아보겠습니다.

Crashlytics?

App에서 crash와 error가 발생했을 때 이에 대한 자세한 내용과 분석을 수집하는 것을 도와줍니다.

3가지 측면을 통해서 이루어집니다.

  1. Log : App에서 충돌이 발생했을 때 log event가 crash report와 함께 전송됩니다.
  2. Crash reports : 모든 충돌은 자동적으로 crash report로 전환되고, app이 다음에 켜질때 전송됩니다.
  3. Stack traces : error가 잡하기 app이 복구되었을 때에도 Dart stack trace는 여전히 보내질 수 있습니다.
    • stack trace?
      exception이 발생하였을 때 프로그램이 실행중에 호출한 메소드의 리스트 입니다.

[Firebase Crashlytics 소개]

배경
앱은 다양한 충돌을 유발하기도 하며 메뉴얼에 따라 추적하는 일도 시간이 낭비됩니다.
모든 데이터를 수집할 수 있다고 해도 문제가 된 충돌 지점을 파악하고 고치는 일도 쉽지 않습니다.

Firebase Crashlytics
Firebase Crashlytics는 자동으로 이것들을 수집하고 분석하여 crash report를 작성합니다.
어떤 문제가 가장 중요한지 파악해주고 우선화해줍니다.

리얼타임 대시보드를 결합하여 새로운 이슈와 갑작스러운 앱의 불안정성을 알수 있도록 합니다.
대시보드는 사용자에 대한 전반적인 영향에 따라서 crash 순위를 보여줍니다.

충돌은 앱 버전별로 그룹화되고 stack trace는 대시보드에서 쉽게 볼 수 있습니다.
Crashlytics의 로그와 (키, 밸류)를 이용하여 각 stack trace에 컨텍스트를 추가할 수 있고, 충돌 전에 앱에서 어떤 작업을 하고 있었는지 알 수 있습니다다.

충돌이 보고되면 이런 정보들은 Crashlytics로 보내지고, 대시보드에 표시되며 충돌의 정확한 원인을 집어주는 내용을 전송합니다. 치명적이지 않은 오류도 대시보드에 보고될 수 있습니다. 코드가 실패했을때, app이 충돌하지는 않지만, 대시보드에서 전체 보고서를 볼 수 있습니다.

App이 android ndk로 만들어졌으면 네이티브 stack trace도 잡아낼 수 있습니다. Crashlytics는 Android와 iOS에서 모두 쉽게 통합할 수 있습니다.



Installation

  1. pubspec.yaml에 firebase_crashlytics를 추가합니다.


Platform Setup

Android와 iOS 설정을 각각 진행합니다.

Android

android/build.gradle 파일에서 사진처럼 classpath를 추가합니다.

android/app/build.gradle 파일에서 apply plugin을 추가합니다.

iOS

Xcode를 열고 Runner → Build Phases tap → + button → New Run Script Phase를 선택합니다.

Run Script를 열고 ${PODS_ROOT}/FirebaseCrashlytics/run 를 작성합니다.



Usage

Firebase Console → Crashlytics에서 Crashlytics 사용 설정을 클릭합니다.

Crashlytics로 report 보내기

Crashlytics로 데이터를 보내기 위해서는 반드시 app이 재실행되어야 합니다.
Crashlytics는 app이 다음에 다시 시작되었을 때 자동으로 crash report를 Firebase로 전송합니다.

Crashlytics collection 전환하기

setCrashlyticsCollectionEnabled를 이용하여 Crashlytics collection status를 전환할 수 있습니다.
예를 들어 디버그 모드에서 crash를 수집하고 싶지 않은경우에 setCrashlyticsCollectionEnabled(false)를 호출합니다.

import 'package:flutter/foundation.dart' show kDebugMode;


if (kDebugMode) {
  await FirebaseCrashlytics.instance
      .setCrashlyticsCollectionEnabled(false);
} else {
	...
}

현재 상태가 수집가능한지 아닌지도 확인 가능합니다.

if (FirebaseCrashlytics.instance.isCrashlyticsCollectionEnabled) {
	...
}

Crash 강제로 발생시키기

crash 메소드를 호출하여 강제로 crash를 발생시킬 수 있고, app을 다시 실행시켰을 때 Crashlytics가 Firebase Console로 crash report를 전송합니다.

FirebaseCrashlytics.instance.crash();

Crash types

  1. Fatal crash
    치명적인 에러를 기록하고 싶으면 fatal argument를 true로 합니다. 대시보드에서 event type은 Crash 로 되어 있고 stack trace를 보면 Fatal Exception 으로 되어있을 것입니다.
    await FirebaseCrashlytics.instance.recordError(
      error, 
      stackTrace,
      reason: 'a fatal error',
      fatal: true
    );
  1. Non-Fatal crash
    default로 non-fatal error가 기록됩니다. 대시보드에서 event type은 Non-fatal 로 되어 있고 stack trace를 보면 Non-Fatal Exception 으로 되어있을 것입니다.
    await FirebaseCrashlytics.instance.recordError(
      error, 
      stackTrace,
      reason: 'a non-fatal error'
    );

Custom Key 추가하기

setCustomKey 메소드를 이용하여 (키, 밸류) 쌍으로 crash report를 연결해줄 수 있습니다.
** 최대 64개의 쌍을 만들 수 있고, 제한을 넘어가는 것들은 무시된다. 키, 밸류는 1024자 까지 작성할 수 있습니다.

// Set a key to a string.
FirebaseCrashlytics.instance.setCustomKey('str_key', 'hello');

// Set a key to a boolean.
FirebaseCrashlytics.instance.setCustomKey("bool_key", true);

// Set a key to an int.
FirebaseCrashlytics.instance.setCustomKey("int_key", 1);

// Set a key to a long.
FirebaseCrashlytics.instance.setCustomKey("int_key", 1L);

// Set a key to a float.
FirebaseCrashlytics.instance.setCustomKey("float_key", 1.0f);

// Set a key to a double.
FirebaseCrashlytics.instance.setCustomKey("double_key", 1.0);

Custom Log Message 추가하기

log 메소드를 이용하여 custom log message를 추가할 수 있습니다.

FirebaseCrashlytics.instance.log("Higgs-Boson detected! Bailing out");

User identifiers 설정

report에 user ID를 추가하여 각 user에 대한 uniqe ID를 부여할 수 있습니다. ID는 숫자, token, hash값이 될 수 있습니다.

FirebaseCrashlytics.instance.setUserIdentifier("12345");

Handling uncaught errors

FlutterError.onErrorFirebaseCrashlytics.instance.recordFlutterError로 override하여, Flutter 내에서 발생하는 모든 error를 catch할 수 있습니다.

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  
  await Firebase.initializeApp();

  FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterError;
  runApp(MyApp());
}

Zoned Errors

버튼의 onPressed같은 곳에서 발생하는 exception은 Flutter에 의해서 catch되지않을 것입니다. 이러한 error를 catch하기 위해서 runZonedGuarded를 사용할 수 있습니다. 여기서 WidgetsFlutterBinding.ensureInitialized()은 반드시 runZonedGuarded 내부에서 호출되어야합니다.

void main() async {
  runZonedGuarded<Future<void>>(() async {
    WidgetsFlutterBinding.ensureInitialized();
    await Firebase.initializeApp();
    
    FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterError;

    runApp(MyApp());
  }, (error, stack) => FirebaseCrashlytics.instance.recordError(error, stack));
}

Errors outside of Flutter

Flutter context 외부에서 발생하는 error를 catch하기 위해서는 current isolate에서 addErrorListener를 사용해야합니다.

Isolate.current.addErrorListener(RawReceivePort((pair) async {
  final List<dynamic> errorAndStacktrace = pair;
  await FirebaseCrashlytics.instance.recordError(
    errorAndStacktrace.first,
    errorAndStacktrace.last,
  );
}).sendPort);

opt-in reporting 기능

defalut로 Crashlytics는 자동으로 모든 유저에 대해서 crash report를 수집합니다. opt-in reporting 기능을 이용하여 자동으로 데이터를 수집하는 것을 막고 선택된 유저에 대해서만 Crashlytics를 initialze할 수 있습니다.

  1. native에서 자동 수집 끄기
    a. Android
    AndroidManifest.xml에 meta-data를 추가합니다.

        <meta-data android:name="firebase_crashlytics_collection_enabled" android:value="false" />

    b. iOS
    Info.plist 에 key, value를 추가합니다.
    Key : FirebaseCrashlyticsCollectionEnabled
    Value : false

  2. 런타임에서 CrashCrashlytics data collection override를 호출해서 선택된 유저에 대해서 수집을 하도록 합니다. 자동으로 데이터 수집하는 것을 허용하지 않으려면 false로 설정합니다. false로 설정했을 때 app이 다음에 다시 시작할때까지 새로운 값이 적용되지 않습니다.

recordError method

recordError를 호출해서 앱의 비정상 종료 외에도 catch된 error의 crash report를 전송할 수 있습니다.

FirebaseCrashlytics.instance.recordError( 
	exception, 
	stackTrace, 
	reason: "Name of screen that cused the error", 
);

FirebaseCrashlytics.instance.recordError( 
	exception, 
	stackTrace, 
	reason: textFieldController.text == null ? "No input from user" : textFieldController.text, 
);


Crashlytics in the Firebase Console

Crashlytics 환경구축을 한번 하면 Firebase console에서 확인할 수 있습니다.
Crashlytics console의 issue 부분에서는 앱에서 전송된 여러 report를 확인할 수 있습니다.

Filtering crash types

event type( Crash, Non-fatal)에 따라 필터링할 수 있다.

Managing issues

issue를 클릭하면 더 자세한 분석이나 통계를 확인할 수 있습니다. app version, device, os version 통계를 확인할 수 있습니다.



Closing issues

issue를 수정한 후, 우측 상단에 있는 close버튼을 누르면 issue를 close 상태로 변경할 수 있습니다.
같은 issue가 다시 발생하면 자동으로 open 상태로 변경됩니다. 자동으로 open되지 않도록 하려면 mute로 설정합니다.





[Code]

Reference

[FlutterFire - Crashlytics]

[Firebase Crashlytics for Flutter]

1개의 댓글

comment-user-thumbnail
2023년 5월 24일

글 잘 읽었습니다. 한가지 궁금한 점이 있습니다. runZonedGuarded을 사용할 경우 WidgetsFlutterBinding.ensureInitialized()은 반드시 runZonedGuarded 내부에서 호출되어야 한다고 하셨는데 그 이유가 있을까요?

답글 달기