|
| 1 | +--- |
| 2 | +layout: page |
| 3 | +title: "Report errors to a service" |
| 4 | +permalink: /cookbook/maintenance/error-reporting/ |
| 5 | +--- |
| 6 | + |
| 7 | +While we always do our best to create apps that are free of bugs, they're sure |
| 8 | +to crop up from time to time. Since buggy apps lead to unhappy |
| 9 | +users and customers, it's important to understand how often our users experience |
| 10 | +bugs and where those bugs occur. That way, we can prioritize the bugs with the |
| 11 | +highest impact and work to fix them. |
| 12 | + |
| 13 | +How can we determine how often our users experiences bugs? Whenever an error |
| 14 | +occurs, we can create a report containing the error that occurred and the |
| 15 | +associated stacktrace. We can then send the report to an error tracking service, |
| 16 | +such as Sentry, Fabric, or Rollbar. |
| 17 | + |
| 18 | +The error tracking service will then aggregate all of the crashes our users |
| 19 | +experience and group them together for us. This allows us to know how often our |
| 20 | +app fails and where our users run into trouble. |
| 21 | + |
| 22 | +In this recipe, we'll see how to report errors to the |
| 23 | +[Sentry](https://sentry.io/welcome/) crash reporting service. |
| 24 | + |
| 25 | +## Directions |
| 26 | + |
| 27 | + 1. Get a DSN from Sentry |
| 28 | + 2. Import the Sentry package |
| 29 | + 3. Create a `SentryClient` |
| 30 | + 4. Create a function to report errors |
| 31 | + 5. Catch and report Dart errors |
| 32 | + 6. Catch and report Flutter errors |
| 33 | + |
| 34 | +## 1. Get a DSN from Sentry |
| 35 | + |
| 36 | +Before we can report errors to Sentry, we'll need a "DSN" to uniquely identify |
| 37 | +our app with the Sentry.io service. |
| 38 | + |
| 39 | +To get a DSN, please: |
| 40 | + |
| 41 | + 1. [Create an account with Sentry](https://sentry.io/signup/) |
| 42 | + 2. Log in to the account |
| 43 | + 3. Create a new app |
| 44 | + 4. Copy the DSN |
| 45 | + |
| 46 | +## 2. Import the Sentry package |
| 47 | + |
| 48 | +Next, we'll need to import the |
| 49 | +[`sentry`](https://pub.dartlang.org/packages/sentry) package into our app. The |
| 50 | +sentry package will make it easier for us to send error reports to the Sentry |
| 51 | +error tracking service. |
| 52 | + |
| 53 | +```yaml |
| 54 | +dependencies: |
| 55 | + sentry: <latest_version> |
| 56 | +``` |
| 57 | +
|
| 58 | +## 3. Create a `SentryClient` |
| 59 | + |
| 60 | +We can now create a `SentryClient`. We will use the `SentryClient` to send |
| 61 | +error reports to the sentry service! |
| 62 | + |
| 63 | +<!-- skip --> |
| 64 | +```dart |
| 65 | +final SentryClient _sentry = new SentryClient(dsn: "App DSN goes Here"); |
| 66 | +``` |
| 67 | + |
| 68 | +## 4. Create a function to report errors |
| 69 | + |
| 70 | +With Sentry all set up, we can begin to report errors! Since we don't want to |
| 71 | +report errors to Sentry during development, we'll first create a function that |
| 72 | +let's us know whether we're in debug or production mode. |
| 73 | + |
| 74 | +<!-- skip --> |
| 75 | +```dart |
| 76 | +bool get isInDebugMode { |
| 77 | + // Assume we're in production mode |
| 78 | + bool inDebugMode = false; |
| 79 | + |
| 80 | + // Assert expressions are only evaluated during development. They are ignored |
| 81 | + // in production. Therefore, this code will only turn `inDebugMode` to true |
| 82 | + // in our development environments! |
| 83 | + assert(inDebugMode = true); |
| 84 | + |
| 85 | + return inDebugMode; |
| 86 | +} |
| 87 | +``` |
| 88 | + |
| 89 | +Next, we can use this function in combination with the `SentryClient` to report |
| 90 | +errors when our app is in production mode. |
| 91 | + |
| 92 | +<!-- skip --> |
| 93 | +```dart |
| 94 | +Future<Null> _reportError(dynamic error, dynamic stackTrace) async { |
| 95 | + // Print the exception to the console |
| 96 | + print('Caught error: $error'); |
| 97 | + if (isInDebugMode) { |
| 98 | + // Print the full stacktrace in debug mode |
| 99 | + print(stackTrace); |
| 100 | + return; |
| 101 | + } else { |
| 102 | + // Send the Exception and Stacktrace to Sentry in Production mode |
| 103 | + _sentry.captureException( |
| 104 | + exception: error, |
| 105 | + stackTrace: stackTrace, |
| 106 | + ); |
| 107 | + } |
| 108 | +} |
| 109 | +``` |
| 110 | + |
| 111 | +## 5. Catch and report Dart errors |
| 112 | + |
| 113 | +Now that we have a function to report errors depending on the environment, we |
| 114 | +need a way to capture Dart errors so we can report them! |
| 115 | + |
| 116 | +For this task, we will run our app inside a custom |
| 117 | +[`Zone`](https://docs.flutter.io/flutter/dart-async/Zone-class.html). Zones |
| 118 | +establish an execution context for our code. This provides a convenient way to |
| 119 | +capture all errors that occur within that context by providing an `onError`. |
| 120 | + |
| 121 | +In this case, we'll run our app in a new `Zone` and capture all errors by |
| 122 | +providing an `onError` callback. |
| 123 | + |
| 124 | +<!-- skip --> |
| 125 | +```dart |
| 126 | +runZoned<Future<Null>>(() async { |
| 127 | + runApp(new CrashyApp()); |
| 128 | +}, onError: (error, stackTrace) { |
| 129 | + // Whenever an error occurs, call the `_reportError` function. This will send |
| 130 | + // Dart errors to our dev console or Sentry depending on the environment. |
| 131 | + _reportError(error, stackTrace); |
| 132 | +}); |
| 133 | +``` |
| 134 | + |
| 135 | +## 6. Catch and report Flutter errors |
| 136 | + |
| 137 | +In addition to Dart errors, Flutter can throw additional errors, such as |
| 138 | +platform exceptions that occur when calling native code. We need to be sure to |
| 139 | +capture and report these types of errors as well! |
| 140 | + |
| 141 | +To capture Flutter errors, we can override the |
| 142 | +[`FlutterError.onError`](https://docs.flutter.io/flutter/foundation/FlutterError/onError.html) |
| 143 | +property. In this case, if we're in debug mode, we'll use a convenience function |
| 144 | +from Flutter to properly format the error. If we're in production mode, we'll |
| 145 | +send the error to our `onError` callback defined in the previous step. |
| 146 | + |
| 147 | +<!-- skip --> |
| 148 | +```dart |
| 149 | +// This captures errors reported by the Flutter framework. |
| 150 | +FlutterError.onError = (FlutterErrorDetails details) { |
| 151 | + if (isInDebugMode) { |
| 152 | + // In development mode simply print to console. |
| 153 | + FlutterError.dumpErrorToConsole(details); |
| 154 | + } else { |
| 155 | + // In production mode report to the application zone to report to |
| 156 | + // Sentry. |
| 157 | + Zone.current.handleUncaughtError(details.exception, details.stack); |
| 158 | + } |
| 159 | +}; |
| 160 | +``` |
| 161 | + |
| 162 | +## Complete Example |
| 163 | + |
| 164 | +To view a working example, please view the |
| 165 | +[Crashy](https://github.com/flutter/crashy) example app. |
0 commit comments