diff --git a/CHANGELOG.md b/CHANGELOG.md index a6e7e72366..bdf802839f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Unreleased +* Feat: Add current route as transaction (#560) + # 6.0.0-beta.4 ## Breaking Changes: diff --git a/dart/lib/src/enricher/web_enricher_event_processor.dart b/dart/lib/src/enricher/web_enricher_event_processor.dart index 2dd78fd166..7786ed8276 100644 --- a/dart/lib/src/enricher/web_enricher_event_processor.dart +++ b/dart/lib/src/enricher/web_enricher_event_processor.dart @@ -29,6 +29,7 @@ class WebEnricherEventProcessor extends EventProcessor { return event.copyWith( contexts: contexts, request: _getRequest(event.request), + transaction: event.transaction ?? _window.location.pathname, ); } diff --git a/dart/test/enricher/web_enricher_test.dart b/dart/test/enricher/web_enricher_test.dart index da9e0f24ae..28337b1bda 100644 --- a/dart/test/enricher/web_enricher_test.dart +++ b/dart/test/enricher/web_enricher_test.dart @@ -16,6 +16,20 @@ void main() { fixture = Fixture(); }); + test('add path as transaction if transaction is null', () async { + var enricher = fixture.getSut(); + final event = await enricher.apply(SentryEvent()); + + expect(event.transaction, isNotNull); + }); + + test("don't overwrite transaction", () async { + var enricher = fixture.getSut(); + final event = await enricher.apply(SentryEvent(transaction: 'foobar')); + + expect(event.transaction, 'foobar'); + }); + test('add request with user-agent header', () async { var enricher = fixture.getSut(); final event = await enricher.apply(SentryEvent()); diff --git a/flutter/example/lib/main.dart b/flutter/example/lib/main.dart index a66a4efc67..738f80eaa7 100644 --- a/flutter/example/lib/main.dart +++ b/flutter/example/lib/main.dart @@ -418,6 +418,12 @@ class SecondaryScaffold extends StatelessWidget { Navigator.pop(context); }, ), + MaterialButton( + child: const Text('throw uncaught exception'), + onPressed: () { + throw Exception('Exception from SecondaryScaffold'); + }, + ), ], ), ), diff --git a/flutter/lib/src/navigation/sentry_navigator_observer.dart b/flutter/lib/src/navigation/sentry_navigator_observer.dart index 917bc67c08..620efcc351 100644 --- a/flutter/lib/src/navigation/sentry_navigator_observer.dart +++ b/flutter/lib/src/navigation/sentry_navigator_observer.dart @@ -35,13 +35,16 @@ const _navigationKey = 'navigation'; /// - [RouteObserver](https://api.flutter.dev/flutter/widgets/RouteObserver-class.html) /// - [Navigating with arguments](https://flutter.dev/docs/cookbook/navigation/navigate-with-arguments) class SentryNavigatorObserver extends RouteObserver> { - SentryNavigatorObserver({Hub? hub}) : hub = hub ?? HubAdapter(); + SentryNavigatorObserver({Hub? hub, this.setTransaction = true}) + : _hub = hub ?? HubAdapter(); - final Hub hub; + final Hub _hub; + final bool setTransaction; @override void didPush(Route route, Route? previousRoute) { super.didPush(route, previousRoute); + setCurrentRoute(route.settings.name); _addBreadcrumb( type: 'didPush', from: previousRoute?.settings, @@ -52,7 +55,7 @@ class SentryNavigatorObserver extends RouteObserver> { @override void didReplace({Route? newRoute, Route? oldRoute}) { super.didReplace(newRoute: newRoute, oldRoute: oldRoute); - + setCurrentRoute(newRoute?.settings.name); _addBreadcrumb( type: 'didReplace', from: oldRoute?.settings, @@ -63,7 +66,7 @@ class SentryNavigatorObserver extends RouteObserver> { @override void didPop(Route route, Route? previousRoute) { super.didPop(route, previousRoute); - + setCurrentRoute(previousRoute?.settings.name); _addBreadcrumb( type: 'didPop', from: route.settings, @@ -76,12 +79,20 @@ class SentryNavigatorObserver extends RouteObserver> { RouteSettings? from, RouteSettings? to, }) { - hub.addBreadcrumb(RouteObserverBreadcrumb( + _hub.addBreadcrumb(RouteObserverBreadcrumb( navigationType: type, from: from, to: to, )); } + + void setCurrentRoute(String? name) { + if (setTransaction) { + _hub.configureScope((scope) { + scope.transaction = name; + }); + } + } } /// This class makes it easier to record breadcrumbs for events of Flutters diff --git a/flutter/test/sentry_navigator_observer_test.dart b/flutter/test/sentry_navigator_observer_test.dart index fd08c2a61d..9ccf1f62c2 100644 --- a/flutter/test/sentry_navigator_observer_test.dart +++ b/flutter/test/sentry_navigator_observer_test.dart @@ -3,6 +3,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; +import 'mocks.dart'; import 'mocks.mocks.dart'; void main() { @@ -226,5 +227,47 @@ void main() { ).data, ); }); + + test('route name as transaction', () { + final hub = _MockHub(); + final observer = SentryNavigatorObserver(hub: hub, setTransaction: true); + + final to = routeSettings('to'); + final previous = routeSettings('previous'); + + observer.didPush(route(to), route(previous)); + expect(hub.scope.transaction, 'to'); + + observer.didPop(route(to), route(previous)); + expect(hub.scope.transaction, 'previous'); + + observer.didReplace(newRoute: route(to), oldRoute: route(previous)); + expect(hub.scope.transaction, 'to'); + }); + + test('disabled route as transaction', () { + final hub = _MockHub(); + final observer = SentryNavigatorObserver(hub: hub, setTransaction: false); + + final to = routeSettings('to'); + final previous = routeSettings('previous'); + + observer.didPush(route(to), route(previous)); + expect(hub.scope.transaction, null); + + observer.didPop(route(to), route(previous)); + expect(hub.scope.transaction, null); + + observer.didReplace(newRoute: route(to), oldRoute: route(previous)); + expect(hub.scope.transaction, null); + }); }); } + +class _MockHub extends MockHub { + final Scope scope = Scope(SentryOptions(dsn: fakeDsn)); + @override + void configureScope(ScopeCallback? callback) { + callback?.call(scope); + } +}