exception report split for crashlytics

This commit is contained in:
Thibault Deckers 2023-05-23 00:30:07 +02:00
parent 913645953c
commit 75192513f6
9 changed files with 76 additions and 45 deletions

View file

@ -6,8 +6,10 @@ import 'package:aves/model/entry/extensions/props.dart';
import 'package:aves/model/metadata/date_modifier.dart';
import 'package:aves/services/common/services.dart';
import 'package:aves_model/aves_model.dart';
import 'package:aves_report/aves_report.dart';
import 'package:collection/collection.dart';
import 'package:flutter/services.dart';
import 'package:stack_trace/stack_trace.dart';
abstract class MetadataEditService {
Future<Map<String, dynamic>> rotate(AvesEntry entry, {required bool clockwise});
@ -125,44 +127,52 @@ class PlatformMetadataEditService implements MetadataEditService {
Future<void> _processPlatformException(AvesEntry entry, PlatformException e, StackTrace stack) async {
if (entry.isValid) {
final code = e.code;
final customException = CustomPlatformException.fromStandard(e);
if (code.endsWith('mp4largemoov')) {
await reportService.recordError(_Mp4LargeMoovException(code: e.code, message: e.message, details: e.details, stacktrace: e.stacktrace), stack);
await mp4LargeMoov(customException);
} else if (code.endsWith('mp4largeother')) {
await reportService.recordError(_Mp4LargeOtherException(code: e.code, message: e.message, details: e.details, stacktrace: e.stacktrace), stack);
await mp4LargeOther(customException);
} else if (code.endsWith('filenotfound')) {
await reportService.recordError(_FileNotFoundException(code: e.code, message: e.message, details: e.details, stacktrace: e.stacktrace), stack);
await fileNotFound(customException);
} else {
await reportService.recordError(e, stack);
}
}
}
StackTrace? _currentStack() => ReportService.buildReportStack(Trace.current(), level: 1);
// distinct exceptions to convince Crashlytics to split reports into distinct issues
Future<void> mp4LargeMoov(CustomPlatformException e) => reportService.recordError(e, _currentStack());
Future<void> mp4LargeOther(CustomPlatformException e) => reportService.recordError(e, _currentStack());
Future<void> fileNotFound(CustomPlatformException e) => reportService.recordError(e, _currentStack());
}
// distinct exceptions to convince Crashlytics to split reports into distinct issues
class CustomPlatformException {
final String code;
final String? message;
final dynamic details;
final String? stacktrace;
class _Mp4LargeMoovException extends PlatformException {
_Mp4LargeMoovException({
required super.code,
required super.message,
required super.details,
required super.stacktrace,
CustomPlatformException({
required this.code,
this.message,
this.details,
this.stacktrace,
});
}
class _Mp4LargeOtherException extends PlatformException {
_Mp4LargeOtherException({
required super.code,
required super.message,
required super.details,
required super.stacktrace,
});
}
factory CustomPlatformException.fromStandard(PlatformException e) {
return CustomPlatformException(
code: e.code,
message: e.message,
details: e.details,
stacktrace: e.stacktrace,
);
}
class _FileNotFoundException extends PlatformException {
_FileNotFoundException({
required super.code,
required super.message,
required super.details,
required super.stacktrace,
});
@override
String toString() => '$runtimeType($code, $message, $details, $stacktrace)';
}

View file

@ -1,6 +1,8 @@
library aves_report;
import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart';
import 'package:stack_trace/stack_trace.dart';
abstract class ReportService {
Future<void> init();
@ -18,4 +20,18 @@ abstract class ReportService {
Future<void> recordError(dynamic exception, StackTrace? stack);
Future<void> recordFlutterError(FlutterErrorDetails flutterErrorDetails);
static StackTrace? buildReportStack(StackTrace stack, {int level = 0}) {
// simply creating a trace with `Trace.current(1)` or creating a `Trace` from modified frames
// does not yield a stack trace that Crashlytics can segment,
// so we reconstruct a string stack trace instead
return StackTrace.fromString(Trace.from(stack)
.frames
.skip(level)
.toList()
.mapIndexed(
(i, f) => '#${(i++).toString().padRight(8)}${f.member} (${f.uri}:${f.line}:${f.column})',
)
.join('\n'));
}
}

View file

@ -10,7 +10,7 @@ packages:
source: hosted
version: "1.3.0"
collection:
dependency: transitive
dependency: "direct main"
description:
name: collection
sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
@ -62,11 +62,27 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.9.1"
path:
dependency: transitive
description:
name: path
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
url: "https://pub.dev"
source: hosted
version: "1.8.3"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.99"
stack_trace:
dependency: "direct main"
description:
name: stack_trace
sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
url: "https://pub.dev"
source: hosted
version: "1.11.0"
vector_math:
dependency: transitive
description:

View file

@ -8,6 +8,8 @@ environment:
dependencies:
flutter:
sdk: flutter
collection:
stack_trace:
dev_dependencies:
flutter_lints:

View file

@ -3,12 +3,10 @@ library aves_report_platform;
import 'dart:async';
import 'package:aves_report/aves_report.dart';
import 'package:collection/collection.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:stack_trace/stack_trace.dart';
class PlatformReportService extends ReportService {
FirebaseCrashlytics? get _instance {
@ -71,17 +69,7 @@ class PlatformReportService extends ReportService {
@override
Future<void> recordError(dynamic exception, StackTrace? stack) async {
if (exception is PlatformException && stack != null) {
// simply creating a trace with `Trace.current(1)` or creating a `Trace` from modified frames
// does not yield a stack trace that Crashlytics can segment,
// so we reconstruct a string stack trace instead
stack = StackTrace.fromString(Trace.from(stack)
.frames
.skip(2)
.toList()
.mapIndexed(
(i, f) => '#${(i++).toString().padRight(8)}${f.member} (${f.uri}:${f.line}:${f.column})',
)
.join('\n'));
stack = ReportService.buildReportStack(stack, level: 2);
}
return _instance?.recordError(exception, stack);
}

View file

@ -49,7 +49,7 @@ packages:
source: hosted
version: "1.1.1"
collection:
dependency: "direct main"
dependency: transitive
description:
name: collection
sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
@ -197,7 +197,7 @@ packages:
source: hosted
version: "1.9.1"
stack_trace:
dependency: "direct main"
dependency: transitive
description:
name: stack_trace
sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5

View file

@ -10,12 +10,10 @@ dependencies:
sdk: flutter
aves_report:
path: ../aves_report
collection:
# as of `firebase_core` v2.10.0, upgrading packages downgrades `firebase_core` et al.
# so that the transitive `path` gets upgraded to v1.8.3
firebase_core: ">=2.10.0"
firebase_crashlytics:
stack_trace:
dev_dependencies:
flutter_lints:

View file

@ -1283,7 +1283,7 @@ packages:
source: hosted
version: "2.4.5"
stack_trace:
dependency: transitive
dependency: "direct main"
description:
name: stack_trace
sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5

View file

@ -108,6 +108,7 @@ dependencies:
shared_preferences:
smooth_page_indicator:
sqflite:
stack_trace:
streams_channel:
git:
url: https://github.com/deckerst/aves_streams_channel.git