From 9901ea00c7d56bcc3eaeb7338e68370c26356e3e Mon Sep 17 00:00:00 2001 From: Miguel Beltran Date: Tue, 27 Aug 2024 08:04:12 +0200 Subject: [PATCH] feat: #30 add innerError to sendCustom method (#195) * feat: #30 add innerError to sendCustom method * update readme * remove TODO --- README.md | 3 +++ example/lib/main.dart | 14 +++++++++++++- lib/raygun4flutter.dart | 13 ++++++++++++- lib/src/messages/raygun_error_message.dart | 8 +++++++- lib/src/raygun_crash_reporting.dart | 13 ++++++++++++- test/raygun4flutter_test.dart | 18 ++++++++++++++++++ 6 files changed, 65 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 80e7822..cd9c02b 100644 --- a/README.md +++ b/README.md @@ -142,6 +142,8 @@ All arguments but `error` are optional. This method is mainly a convenience wrap Call `Raygun.sendCustom(className, reason, tags, customData, stackTrace)` to send custom errors to Raygun with your own customised `className` and `reason`. As with `.sendException()`, `tags`, `customData` and `stackTrace` are optional. +You can also provide an optional `innerError` `Exception` object that will be attached to the error report. + For example: ```dart @@ -154,6 +156,7 @@ Raygun.sendCustom( 'custom2': 42, }, stackTrace: StackTrace.current, + innerError: Exception('Error!'), ); ``` diff --git a/example/lib/main.dart b/example/lib/main.dart index bcd8120..71ca95e 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -142,9 +142,10 @@ class _MyAppState extends State { 'custom2': 42, }, stackTrace: StackTrace.current, + innerError: const MyCustomException('Custom exception'), ); }, - child: const Text('Send custom error with tags and customData'), + child: const Text('Send custom error with all options'), ), // example: Button tap adds breadcrumb to future error message @@ -199,3 +200,14 @@ class _MyAppState extends State { ); } } + +class MyCustomException implements Exception { + const MyCustomException(this.message); + + final String message; + + @override + String toString() { + return 'MyCustomException: $message'; + } +} diff --git a/lib/raygun4flutter.dart b/lib/raygun4flutter.dart index e243172..d615490 100644 --- a/lib/raygun4flutter.dart +++ b/lib/raygun4flutter.dart @@ -94,12 +94,16 @@ class Raygun { /// /// [stackTrace] optional parameter, if not provided this method will obtain /// the current stacktrace automatically. + /// + /// [innerError] optional [Exception] to attach as "innerError" + /// in the error report. static Future sendCustom({ required String className, required String reason, List? tags, Map? customData, StackTrace? stackTrace, + Exception? innerError, }) async { Trace trace; if (stackTrace == null) { @@ -109,7 +113,14 @@ class Raygun { trace = Trace.from(stackTrace); } - return CrashReporting.send(className, reason, tags, customData, trace); + return CrashReporting.send( + className, + reason, + tags, + customData, + trace, + innerError, + ); } /// Sets a List of tags which will be sent along with every exception. diff --git a/lib/src/messages/raygun_error_message.dart b/lib/src/messages/raygun_error_message.dart index 16d44ca..0594898 100644 --- a/lib/src/messages/raygun_error_message.dart +++ b/lib/src/messages/raygun_error_message.dart @@ -10,7 +10,6 @@ class RaygunErrorMessage { String message; String className; - // todo: innerError is null at the moment RaygunErrorMessage? innerError; List stackTrace = []; @@ -33,6 +32,13 @@ class RaygunErrorMessage { .toList(); } + factory RaygunErrorMessage.fromException(Exception exception) { + return RaygunErrorMessage( + exception.runtimeType.toString(), + exception.toString(), + ); + } + factory RaygunErrorMessage.fromJson( Map json, ) => diff --git a/lib/src/raygun_crash_reporting.dart b/lib/src/raygun_crash_reporting.dart index 71d005f..6a224c2 100644 --- a/lib/src/raygun_crash_reporting.dart +++ b/lib/src/raygun_crash_reporting.dart @@ -22,8 +22,14 @@ class CrashReporting { List? tags, Map? customData, Trace? trace, + Exception? innerError, ) async { - final RaygunMessage msg = await _buildMessage(className, reason, trace); + final RaygunMessage msg = await _buildMessage( + className, + reason, + trace, + innerError, + ); msg.details.tags = tags ?? []; final globalTags = Settings.tags; @@ -70,6 +76,7 @@ Future _buildMessage( String className, String reason, Trace? trace, + Exception? innerError, ) async { final raygunMessage = RaygunMessage(); @@ -77,6 +84,10 @@ Future _buildMessage( if (trace != null) { raygunMessage.details.error!.setStackTrace(trace); } + if (innerError != null) { + raygunMessage.details.error!.innerError = + RaygunErrorMessage.fromException(innerError); + } raygunMessage.details.client = RaygunClientMessage(); raygunMessage.details.breadcrumbs.addAll(Settings.breadcrumbs); diff --git a/test/raygun4flutter_test.dart b/test/raygun4flutter_test.dart index 2b5d3e2..7578e7b 100644 --- a/test/raygun4flutter_test.dart +++ b/test/raygun4flutter_test.dart @@ -75,6 +75,24 @@ void main() { expect(capturedBody['details']['userCustomData'], {'custom': 42}); }); + test('sendCustom with innerError', () async { + await Raygun.sendCustom( + className: 'CLASSNAME', + reason: 'REASON', + innerError: Exception('MESSAGE'), + ); + + // main error should be custom className and reason + expect(capturedBody['details']['error']['message'], 'REASON'); + expect(capturedBody['details']['error']['className'], 'CLASSNAME'); + + // innerError should be exception message and type + expect(capturedBody['details']['error']['innerError']['message'], + 'Exception: MESSAGE'); + expect(capturedBody['details']['error']['innerError']['className'], + '_Exception'); + }); + test('Breadcrumb', () async { Raygun.recordBreadcrumb('BREADCRUMB'); final breadcrumbMessage = RaygunBreadcrumbMessage(