Sentry improvements

This commit is contained in:
Hillel Coren 2020-03-27 15:03:03 +03:00
parent b67a5d77fd
commit ffdd96c9f1
6 changed files with 92 additions and 27 deletions

View File

@ -1,5 +1,4 @@
class Config { class Config {
static const String PLATFORM = 'web';
static const String API_SECRET = 'secret'; static const String API_SECRET = 'secret';
static const String SENTRY_DNS = 'dns'; static const String SENTRY_DNS = 'dns';

View File

@ -1,6 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:core'; import 'dart:core';
import 'dart:io';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:invoiceninja_flutter/.env.dart'; import 'package:invoiceninja_flutter/.env.dart';
@ -33,7 +34,7 @@ class AuthRepository {
'last_name': lastName, 'last_name': lastName,
'terms_of_service': true, 'terms_of_service': true,
'privacy_policy': true, 'privacy_policy': true,
'token_name': '${Config.PLATFORM.toLowerCase()}-client', 'token_name': _tokenName,
}; };
if ((url ?? '').isEmpty) { if ((url ?? '').isEmpty) {
@ -91,7 +92,7 @@ class AuthRepository {
Future<dynamic> addCompany({String token}) async { Future<dynamic> addCompany({String token}) async {
final data = { final data = {
'token_name': '${Config.PLATFORM.toLowerCase()}-client', 'token_name': _tokenName,
}; };
return webClient.post('/companies', token, data: json.encode(data)); return webClient.post('/companies', token, data: json.encode(data));
@ -128,4 +129,7 @@ class AuthRepository {
return serializers.deserializeWith(LoginResponse.serializer, response); return serializers.deserializeWith(LoginResponse.serializer, response);
} }
String get _tokenName =>
kIsWeb ? 'web' : Platform.isAndroid ? 'android' : 'ios';
} }

View File

@ -45,6 +45,7 @@ import 'package:invoiceninja_flutter/ui/settings/tax_settings_vm.dart';
import 'package:invoiceninja_flutter/utils/colors.dart'; import 'package:invoiceninja_flutter/utils/colors.dart';
import 'package:invoiceninja_flutter/utils/localization.dart'; import 'package:invoiceninja_flutter/utils/localization.dart';
import 'package:invoiceninja_flutter/utils/platforms.dart'; import 'package:invoiceninja_flutter/utils/platforms.dart';
import 'package:invoiceninja_flutter/utils/sentry.dart';
import 'package:local_auth/local_auth.dart'; import 'package:local_auth/local_auth.dart';
import 'package:redux/redux.dart'; import 'package:redux/redux.dart';
import 'package:redux_logging/redux_logging.dart'; import 'package:redux_logging/redux_logging.dart';
@ -77,16 +78,11 @@ import 'package:invoiceninja_flutter/redux/company_gateway/company_gateway_middl
void main({bool isTesting = false}) async { void main({bool isTesting = false}) async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
//final packageInfo = await PackageInfo.fromPlatform();
final SentryClient _sentry = Config.SENTRY_DNS.isEmpty final SentryClient _sentry = Config.SENTRY_DNS.isEmpty
? null ? null
: SentryClient( : SentryClient(
dsn: Config.SENTRY_DNS, dsn: Config.SENTRY_DNS,
environmentAttributes: Event( environmentAttributes: await getSentryEvent());
//release: packageInfo.version,
release: kAppVersion,
environment: Config.PLATFORM,
));
final store = Store<AppState>(appReducer, final store = Store<AppState>(appReducer,
initialState: await _initialState(isTesting), initialState: await _initialState(isTesting),
@ -121,35 +117,29 @@ void main({bool isTesting = false}) async {
), ),
])); ]));
Future<void> _reportError(dynamic error, dynamic stackTrace) async {
print('Caught error: $error');
if (kDebugMode) {
print(stackTrace);
return;
} else {
_sentry.captureException(
exception: error,
stackTrace: stackTrace,
);
}
}
if (_sentry == null) { if (_sentry == null) {
runApp(InvoiceNinjaApp(store: store)); runApp(InvoiceNinjaApp(store: store));
} else { } else {
runZoned<Future<void>>(() async { runZoned<Future<void>>(() async {
runApp(InvoiceNinjaApp(store: store)); runApp(InvoiceNinjaApp(store: store));
}, onError: (dynamic error, dynamic stackTrace) { }, onError: (dynamic exception, dynamic stackTrace) async {
if (store.state.reportErrors) { if (kDebugMode) {
_reportError(error, stackTrace); print(stackTrace);
} else if (store.state.reportErrors) {
final event = await getSentryEvent(
state: store.state,
exception: exception,
stackTrace: stackTrace,
);
_sentry.capture(event: event);
} }
}); });
} }
FlutterError.onError = (FlutterErrorDetails details) { FlutterError.onError = (FlutterErrorDetails details) {
if (kDebugMode || !store.state.reportErrors) { if (kDebugMode) {
FlutterError.dumpErrorToConsole(details); FlutterError.dumpErrorToConsole(details);
} else { } else if (store.state.reportErrors) {
Zone.current.handleUncaughtError(details.exception, details.stack); Zone.current.handleUncaughtError(details.exception, details.stack);
} }
}; };

64
lib/utils/sentry.dart Normal file
View File

@ -0,0 +1,64 @@
import 'dart:io';
import 'package:device_info/device_info.dart';
import 'package:flutter/foundation.dart';
import 'package:invoiceninja_flutter/constants.dart';
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
import 'package:sentry/sentry.dart';
Future<Event> getSentryEvent(
{AppState state, dynamic exception, dynamic stackTrace}) async {
OperatingSystem os;
Device device;
if (kIsWeb) {
// TODO track web info
os = OperatingSystem(
name: 'Web',
);
} else {
//final packageInfo = await PackageInfo.fromPlatform();
final deviceInfo = DeviceInfoPlugin();
if (Platform.isAndroid) {
final androidInfo = await deviceInfo.androidInfo;
os = OperatingSystem(
name: 'Android',
version: androidInfo.version.release,
);
device = Device(
model: androidInfo.model,
manufacturer: androidInfo.manufacturer,
modelId: androidInfo.product,
);
} else if (Platform.isIOS) {
final iosInfo = await deviceInfo.iosInfo;
os = OperatingSystem(
name: iosInfo.systemName,
version: iosInfo.systemVersion,
);
device = Device(
model: iosInfo.utsname.machine,
manufacturer: 'Apple',
family: iosInfo.model,
);
}
}
return Event(
release: kAppVersion,
//release: packageInfo.version,
environment: state.isHosted ? 'Hosted' : 'Selfhosted',
stackTrace: stackTrace,
exception: exception,
contexts: Contexts(
operatingSystem: os,
device: device,
app: App(
version: kAppVersion,
//name: packageInfo.appName,
//version: packageInfo.version,
//build: packageInfo.buildNumber,
)),
);
}

View File

@ -190,6 +190,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.3.3" version: "1.3.3"
device_info:
dependency: "direct main"
description:
name: device_info
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.2+1"
extended_image: extended_image:
dependency: transitive dependency: transitive
description: description:

View File

@ -43,6 +43,7 @@ dependencies:
flutter_typeahead: ^1.8.0 flutter_typeahead: ^1.8.0
flutter_share: ^1.0.2+1 flutter_share: ^1.0.2+1
package_info: ^0.4.0+16 package_info: ^0.4.0+16
device_info: ^0.4.2+1
#quick_actions: ^0.2.1 #quick_actions: ^0.2.1
dev_dependencies: dev_dependencies: