Skip to content

Instantly share code, notes, and snippets.

@escamoteur
Created June 15, 2018 13:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save escamoteur/87aeff010e1ce4a00116fdd20bcbe842 to your computer and use it in GitHub Desktop.
Save escamoteur/87aeff010e1ce4a00116fdd20bcbe842 to your computer and use it in GitHub Desktop.
///
/// Flutter Captured Error Reporting
/// Created by Simon Lightfoot
///
/// Copyright (C) DevAngels Limited 2018
/// License: APACHE 2.0 - https://www.apache.org/licenses/LICENSE-2.0
///
import 'dart:async';
import 'dart:io';
import 'dart:ui' as ui show window;
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:package_info/package_info.dart';
import 'package:device_info/device_info.dart';
import 'package:system_info/system_info.dart';
import 'package:connectivity/connectivity.dart';
import 'package:sentry/sentry.dart';
const int MEGABYTE = 1024 * 1024;
typedef Widget ErrorReportingWidget(ErrorReporter reporter);
bool get _isInDebugMode {
bool inDebugMode = false;
assert(inDebugMode = true);
return inDebugMode;
}
///
/// Example:
/// void main() => runCapturedApp((reporter) => new AppComponent(reporter), dsn: '<YOUR-DSN>');
///
/// During your app lifecycle you can use the Reporter parameter to report a exception, like this:
/// try {
/// throw new AssertionError();
/// } catch (exception, stackTrace) {
/// errorReporter(exception, stackTrace);
/// }
///
PackageInfo info;
runCapturedApp(ErrorReportingWidget app, {@required String dsn}) async {
final SentryClient sentry = SentryClient(dsn: dsn);
info = await PackageInfo.fromPlatform();
FlutterError.onError = (FlutterErrorDetails details) {
if (_isInDebugMode) {
// In development mode simply print to console.
FlutterError.dumpErrorToConsole(details);
} else {
// In production mode report to the application zone to report to Sentry.
Zone.current.handleUncaughtError(details.exception, details.stack);
}
};
final reporter = new ErrorReporter(sentry);
runZoned(() {
runApp(app(reporter));
}, onError: (Object error, StackTrace stackTrace) async {
await reporter.logException(error, stackTrace);
});
}
class ErrorReporter {
final SentryClient sentry;
ErrorReporter(this.sentry);
Future<Null> logEvent(
String message, SeverityLevel severity, Map<String, dynamic> data) async {
final Event event = Event(
loggerName: '',
message: message,
release: '${info.version}_${info.buildNumber}',
environment: 'qa',
extra: data,
level: severity,
);
try {
final SentryResponse response = await sentry.capture(event: event);
if (response.isSuccessful) {
print('Success! Event ID: ${response.eventId}');
} else {
print('Failed to report to Sentry.io: ${response.error}');
}
} catch (e, stackTrace) {
print(
'Exception whilst reporting to Sentry.io\n' + stackTrace.toString());
}
}
Future<Null> logInfo(String message, [Map<String, dynamic> data]) =>
logEvent(message, SeverityLevel.info, data);
Future<Null> logWarning(String message, [Map<String, dynamic> data]) =>
logEvent(message, SeverityLevel.warning, data);
Future<Null> logException(Object error, StackTrace stackTrace) async {
print('Caught error: $error');
if (_isInDebugMode) {
print(stackTrace);
print('In dev mode. Not sending report to Sentry.io.');
return;
}
print('Reporting to Sentry.io...');
final PackageInfo info = await PackageInfo.fromPlatform();
Map<String, dynamic> extra = {};
if (defaultTargetPlatform == TargetPlatform.android) {
extra['device_info'] =
await DeviceInfoPlugin.channel.invokeMethod('getAndroidDeviceInfo');
} else if (defaultTargetPlatform == TargetPlatform.iOS) {
extra['device_info'] =
await DeviceInfoPlugin.channel.invokeMethod('getIosDeviceInfo');
}
String mode = _isInDebugMode ? 'checked' : 'release';
Map<String, String> tags = {};
tags['platform'] =
defaultTargetPlatform.toString().substring('TargetPlatform.'.length);
tags['package_name'] = info.packageName;
tags['build_number'] = info.buildNumber;
tags['version'] = info.version;
tags['mode'] = mode;
tags['locale'] = ui.window.locale.toString();
ConnectivityResult connectivity =
await (Connectivity().checkConnectivity());
tags['connectivity'] =
connectivity.toString().substring('ConnectivityResult.'.length);
Map<String, dynamic> uiValues = {};
uiValues['locale'] = ui.window.locale.toString();
uiValues['pixel_ratio'] = ui.window.devicePixelRatio;
uiValues['default_route'] = ui.window.defaultRouteName;
uiValues['physical_size'] = [
ui.window.physicalSize.width,
ui.window.physicalSize.height
];
uiValues['text_scale_factor'] = ui.window.textScaleFactor;
uiValues['view_insets'] = [
ui.window.viewInsets.left,
ui.window.viewInsets.top,
ui.window.viewInsets.right,
ui.window.viewInsets.bottom
];
uiValues['padding'] = [
ui.window.padding.left,
ui.window.padding.top,
ui.window.padding.right,
ui.window.padding.bottom
];
if (WidgetsBinding.instance != null) {
// Removed the widget tree as it posts too much information.
/*
if (WidgetsBinding.instance.renderViewElement != null) {
uiValues['render_view'] = WidgetsBinding.instance.renderViewElement.toStringDeep();
} else {
uiValues['render_view'] = '<no tree currently mounted>';
}
*/
}
extra['ui'] = uiValues;
Map<String, dynamic> memory = {};
memory['phys_total'] = '${SysInfo.getTotalPhysicalMemory() ~/ MEGABYTE}MB';
memory['phys_free'] = '${SysInfo.getFreePhysicalMemory() ~/ MEGABYTE}MB';
memory['virt_total'] = '${SysInfo.getTotalVirtualMemory() ~/ MEGABYTE}MB';
memory['virt_free'] = '${SysInfo.getFreeVirtualMemory() ~/ MEGABYTE}MB';
extra['memory'] = memory;
extra['dart_version'] = Platform.version;
final Event event = Event(
loggerName: '',
exception: error,
stackTrace: stackTrace,
release: '${info.version}_${info.buildNumber}',
environment: 'qa',
tags: tags,
extra: extra,
level: SeverityLevel.fatal);
try {
final SentryResponse response = await sentry.capture(event: event);
if (response.isSuccessful) {
print('Success! Event ID: ${response.eventId}');
} else {
print('Failed to report to Sentry.io: ${response.error}');
}
} catch (e, stackTrace) {
print(
'Exception whilst reporting to Sentry.io\n' + stackTrace.toString());
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment