Skip to content

Instantly share code, notes, and snippets.

@escamoteur
Created September 20, 2019 09:24
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/57d37e712befd8bd377f15c85136dcec to your computer and use it in GitHub Desktop.
Save escamoteur/57d37e712befd8bd377f15c85136dcec to your computer and use it in GitHub Desktop.
Helper class to send event to sentry
import 'dart:async';
import 'dart:io';
import 'dart:ui' as ui show window;
import 'package:connectivity/connectivity.dart';
import 'package:device_info/device_info.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:package_info/package_info.dart';
import 'package:sentry/sentry.dart';
import 'package:system_info/system_info.dart';
/// Flutter Captured Error Reporting
/// Created by Simon Lightfoot & Thomas Burkhart
///
/// Copyright (C) DevAngels Limited 2018
/// License: APACHE 2.0 - https://www.apache.org/licenses/LICENSE-2.0
///
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;
void runCapturedApp(Widget app, ErrorReporter reporter) async {
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);
}
};
runZoned(() {
runApp(app);
}, onError: (Object error, StackTrace stackTrace) async {
await reporter.logException(error, stackTrace: stackTrace);
});
}
class ErrorReporter {
SentryClient _sentry;
String dsn;
SentryClient get sentry => _sentry ?? SentryClient(dsn: dsn);
ErrorReporter(this.dsn);
Future<void> 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<void> logInfo(String message, [Map<String, dynamic> data]) =>
logEvent(message, SeverityLevel.info, data);
Future<void> logWarning(String message, [Map<String, dynamic> data]) =>
logEvent(message, SeverityLevel.warning, data);
Future<void> logException(Object error,
{String message, StackTrace stackTrace}) async {
print('Caught error: $error\n$stackTrace');
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 = <String, dynamic>{};
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['message'] = message;
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 = <String, dynamic>{};
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 = <String, dynamic>{};
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());
}
}
}
@devashish-patel
Copy link

devashish-patel commented Dec 9, 2019

void main() => runCapturedApp((reporter) => new AppComponent(reporter), dsn: '');

I do not understand how can I integrate this with the existing app, can you please throw some light on it?

@escamoteur
Copy link
Author

What exactly don't you understand?

@Isuru-Nanayakkara
Copy link

Hi @escamoteur, would you happen to have an updated version of this class? I'm trying to include it in a Flutter project running v2.5.3 and it's throwing all sorts of errors. Some members like SentryResponse, Event don't exist in the latest sentry_flutter package. I updated some packages and fixed some but not sure how to handle the rest (pretty new to Dart and Flutter myself).

This is what I have so far.

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:connectivity/connectivity.dart';
import 'package:device_info/device_info.dart';
import 'package:package_info/package_info.dart';
import 'package:system_info/system_info.dart';
import 'package:sentry/sentry.dart';
import 'package:sentry_flutter/sentry_flutter.dart';

/// Flutter Captured Error Reporting
/// Created by Simon Lightfoot & Thomas Burkhart
///
/// Copyright (C) DevAngels Limited 2018
/// License: APACHE 2.0 - https://www.apache.org/licenses/LICENSE-2.0
///

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;

void runCapturedApp(Widget app, ErrorReporter reporter) async {
  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);
    }
  };
  runZoned(() {
    runApp(app);
  }, onError: (Object error, StackTrace stackTrace) async {
    await reporter.logException(error, stackTrace: stackTrace);
  });
}

class ErrorReporter {
  SentryClient _sentry;
  String dsn;

  SentryClient get sentry => _sentry ?? SentryClient(dsn: dsn);

  ErrorReporter(this.dsn);

  Future<void> 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.captureEvent(event);
      if (response.isSuccessful) {
        print('Success! Event ID: ${response.eventId}');
      } else {
        print('Failed to report to Sentry.io: ${response.error}');
      }
    } catch (e, stackTrace) {
      debugPrint(
          'Exception whilst reporting to Sentry.io\n' + stackTrace.toString());
    }
  }

  Future<void> logInfo(String message, [Map<String, dynamic> data]) =>
      logEvent(message, SeverityLevel.info, data);

  Future<void> logWarning(String message, [Map<String, dynamic> data]) =>
      logEvent(message, SeverityLevel.warning, data);

  Future<void> logException(Object error, {String message, StackTrace stackTrace}) async {
    print('Caught error: $error\n$stackTrace');

    if (_isInDebugMode) {
      debugPrint(stackTrace);
      debugPrint('In dev mode. Not sending report to Sentry.io.');
      return;
    }

    debugPrint('Reporting to Sentry.io...');

    final PackageInfo info = await PackageInfo.fromPlatform();

    Map<String, dynamic> extra = <String, dynamic>{};
    if (defaultTargetPlatform == TargetPlatform.android) {
      extra['device_info'] = DeviceInfoPlugin().androidInfo;
    } else if (defaultTargetPlatform == TargetPlatform.iOS) {
      extra['device_info'] = DeviceInfoPlugin().iosInfo;
    }

    String mode = _isInDebugMode ? 'debug' : 'release';

    Map<String, String> tags = {};
    tags['message'] = message;
    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 = <String, dynamic>{};
    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 = <String, dynamic>{};
    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 = SentryEvent(
        logger: '',
        exceptions: [error],
        exception: error,
        stackTrace: stackTrace,
        release: '${info.version}_${info.buildNumber}',
        environment: 'qa',
        tags: tags,
        extra: extra,
        level: SeverityLevel.fatal);

    try {
      final SentryResponse response = await sentry.captureEvent(event);
      if (response.isSuccessful) {
        debugPrint('Success! Event ID: ${response.eventId}');
      } else {
        debugPrint('Failed to report to Sentry.io: ${response.error}');
      }
    } catch (e, stackTrace) {
      debugPrint(
          'Exception whilst reporting to Sentry.io\n' + stackTrace.toString());
    }
  }
}

@escamoteur
Copy link
Author

Hi, I'm sorry but I don't have the time at the moment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment