Using Logging and Error Reporting Services to Monitor Your Flutter App in Production

Ensuring the stability and performance of a Flutter app in production requires diligent monitoring. Logging and error reporting services are crucial for identifying and resolving issues that arise after deployment. By integrating these services, developers can gain valuable insights into app behavior, track crashes, and address performance bottlenecks promptly.

Why Use Logging and Error Reporting Services?

  • Proactive Issue Detection: Identifies issues before they impact a large number of users.
  • Comprehensive Insights: Provides detailed logs and crash reports to diagnose problems effectively.
  • Improved Stability: Enhances app reliability by addressing and resolving production issues.
  • Performance Monitoring: Tracks performance metrics to identify areas for optimization.

Popular Logging and Error Reporting Services for Flutter

  • Firebase Crashlytics: A robust, free crash reporting solution by Google.
  • Sentry: A comprehensive error tracking and performance monitoring platform.
  • Bugsnag: An advanced error monitoring and crash reporting service.
  • Instabug: A user-friendly platform that includes bug reporting, crash reporting, and in-app feedback.

Implementing Firebase Crashlytics in Flutter

Firebase Crashlytics is a popular choice due to its ease of integration and comprehensive features.

Step 1: Set Up Firebase Project

Create a new project in the Firebase console or use an existing one.

Step 2: Add Firebase to Your Flutter App

Install the required FlutterFire packages:

dependencies:
  firebase_core: ^2.15.0
  firebase_crashlytics: ^3.3.5

Then, initialize Firebase in your main.dart file:

import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:flutter/material.dart';

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

  // Pass all uncaught errors from the framework to Crashlytics.
  FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterError;

  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter Crashlytics Example'),
        ),
        body: Center(
          child: ElevatedButton(
            onPressed: () {
              // Simulate a crash
              FirebaseCrashlytics.instance.crash();
            },
            child: Text('Simulate Crash'),
          ),
        ),
      ),
    );
  }
}

To ensure Crashlytics is initialized correctly, you may also need to configure it in your native Android and iOS projects as described in the Firebase documentation.

Step 3: Configure Native Platforms

Android Configuration
  1. Enable Crashlytics in your Firebase console.
  2. Add the Crashlytics plugin to your project-level build.gradle:
buildscript {
  dependencies {
    classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.9'
  }
}

apply plugin: 'com.google.firebase.crashlytics'

dependencies {
  implementation platform('com.google.firebase:firebase-bom:32.8.0')
  implementation 'com.google.firebase:firebase-analytics'
  implementation 'com.google.firebase:firebase-crashlytics'
}
iOS Configuration
  1. Download the GoogleService-Info.plist file from the Firebase console and add it to your Xcode project.
  2. Add a Run Script phase to your Xcode build process as outlined in the Firebase documentation to upload debug symbols.

Step 4: Log Custom Events and Errors

Use the Firebase Crashlytics API to log custom events, exceptions, and user information:

FirebaseCrashlytics.instance.log('This is a custom log message.');

try {
  // Some code that might throw an exception
  throw Exception('An example exception');
} catch (e, stack) {
  FirebaseCrashlytics.instance.recordError(e, stack, reason: 'An example exception');
}

FirebaseCrashlytics.instance.setUserIdentifier('12345');
FirebaseCrashlytics.instance.setCustomKey('string_key', 'stringValue');
FirebaseCrashlytics.instance.setCustomKey('int_key', 123);
FirebaseCrashlytics.instance.setCustomKey('double_key', 123.45);
FirebaseCrashlytics.instance.setCustomKey('bool_key', true);

Implementing Sentry in Flutter

Sentry offers more advanced features, including performance monitoring and detailed error tracking.

Step 1: Sign Up for Sentry

Create an account on Sentry.io and create a new project.

Step 2: Install Sentry Flutter Package

Add the sentry_flutter package to your pubspec.yaml:

dependencies:
  sentry_flutter: ^7.10.0

Step 3: Initialize Sentry

Initialize Sentry in your main.dart file:

import 'package:flutter/widgets.dart';
import 'package:sentry_flutter/sentry_flutter.dart';

void main() async {
  await SentryFlutter.init(
    (options) {
      options.dsn = 'YOUR_SENTRY_DSN';
      options.tracesSampleRate = 0.2;
    },
    appRunner: () => runApp(MyApp()),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return WidgetsApp(
      builder: (context, widget) {
        return SentryScreenshotWidget(child: widget!);
      },
      color: const Color.fromRGBO(0, 0, 0, 0),
    );
  }
}

Step 4: Capture Exceptions and Messages

Use Sentry’s API to capture exceptions, messages, and breadcrumbs:

try {
  // Some code that might throw an exception
  throw Exception('An example exception');
} catch (e, stackTrace) {
  await Sentry.captureException(
    e,
    stackTrace: stackTrace,
  );
}

Sentry.captureMessage('This is a message', level: SentryLevel.info);

Sentry.addBreadcrumb(
  Breadcrumb(
    message: 'Added a breadcrumb',
    level: SentryLevel.info,
  ),
);

Logging with the logger Package

For general logging, consider using the logger package, which provides a simple and customizable way to log messages at different levels.

Step 1: Add logger Dependency

dependencies:
  logger: ^1.7.1

Step 2: Use the Logger

import 'package:logger/logger.dart';

final logger = Logger();

void main() {
  logger.d('Debug log');
  logger.i('Info log');
  logger.w('Warning log');
  logger.e('Error log');
  logger.wtf('WTF log');
}

Conclusion

Using logging and error reporting services is essential for maintaining a stable and performant Flutter app in production. Services like Firebase Crashlytics and Sentry offer robust features for tracking crashes, logging custom events, and monitoring app performance. By integrating these tools, developers can proactively identify and resolve issues, leading to a better user experience. Whether it’s simple logging with the logger package or comprehensive error tracking with Sentry, incorporating these practices into your Flutter development workflow is highly recommended.