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/model/metadata/date_modifier.dart';
import 'package:aves/services/common/services.dart'; import 'package:aves/services/common/services.dart';
import 'package:aves_model/aves_model.dart'; import 'package:aves_model/aves_model.dart';
import 'package:aves_report/aves_report.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:stack_trace/stack_trace.dart';
abstract class MetadataEditService { abstract class MetadataEditService {
Future<Map<String, dynamic>> rotate(AvesEntry entry, {required bool clockwise}); 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 { Future<void> _processPlatformException(AvesEntry entry, PlatformException e, StackTrace stack) async {
if (entry.isValid) { if (entry.isValid) {
final code = e.code; final code = e.code;
final customException = CustomPlatformException.fromStandard(e);
if (code.endsWith('mp4largemoov')) { 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')) { } 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')) { } 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 { } else {
await reportService.recordError(e, stack); 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 { CustomPlatformException({
_Mp4LargeMoovException({ required this.code,
required super.code, this.message,
required super.message, this.details,
required super.details, this.stacktrace,
required super.stacktrace,
}); });
}
class _Mp4LargeOtherException extends PlatformException { factory CustomPlatformException.fromStandard(PlatformException e) {
_Mp4LargeOtherException({ return CustomPlatformException(
required super.code, code: e.code,
required super.message, message: e.message,
required super.details, details: e.details,
required super.stacktrace, stacktrace: e.stacktrace,
}); );
} }
class _FileNotFoundException extends PlatformException { @override
_FileNotFoundException({ String toString() => '$runtimeType($code, $message, $details, $stacktrace)';
required super.code,
required super.message,
required super.details,
required super.stacktrace,
});
} }

View file

@ -1,6 +1,8 @@
library aves_report; library aves_report;
import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:stack_trace/stack_trace.dart';
abstract class ReportService { abstract class ReportService {
Future<void> init(); Future<void> init();
@ -18,4 +20,18 @@ abstract class ReportService {
Future<void> recordError(dynamic exception, StackTrace? stack); Future<void> recordError(dynamic exception, StackTrace? stack);
Future<void> recordFlutterError(FlutterErrorDetails flutterErrorDetails); 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 source: hosted
version: "1.3.0" version: "1.3.0"
collection: collection:
dependency: transitive dependency: "direct main"
description: description:
name: collection name: collection
sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
@ -62,11 +62,27 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.9.1" version: "1.9.1"
path:
dependency: transitive
description:
name: path
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
url: "https://pub.dev"
source: hosted
version: "1.8.3"
sky_engine: sky_engine:
dependency: transitive dependency: transitive
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.99" 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: vector_math:
dependency: transitive dependency: transitive
description: description:

View file

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

View file

@ -3,12 +3,10 @@ library aves_report_platform;
import 'dart:async'; import 'dart:async';
import 'package:aves_report/aves_report.dart'; import 'package:aves_report/aves_report.dart';
import 'package:collection/collection.dart';
import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart'; import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:stack_trace/stack_trace.dart';
class PlatformReportService extends ReportService { class PlatformReportService extends ReportService {
FirebaseCrashlytics? get _instance { FirebaseCrashlytics? get _instance {
@ -71,17 +69,7 @@ class PlatformReportService extends ReportService {
@override @override
Future<void> recordError(dynamic exception, StackTrace? stack) async { Future<void> recordError(dynamic exception, StackTrace? stack) async {
if (exception is PlatformException && stack != null) { if (exception is PlatformException && stack != null) {
// simply creating a trace with `Trace.current(1)` or creating a `Trace` from modified frames stack = ReportService.buildReportStack(stack, level: 2);
// 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'));
} }
return _instance?.recordError(exception, stack); return _instance?.recordError(exception, stack);
} }

View file

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

View file

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

View file

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

View file

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