Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Unreleased

* Feat: Add current route as transaction (#560)

# 6.0.0-beta.4

## Breaking Changes:
Expand Down
1 change: 1 addition & 0 deletions dart/lib/src/enricher/web_enricher_event_processor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class WebEnricherEventProcessor extends EventProcessor {
return event.copyWith(
contexts: contexts,
request: _getRequest(event.request),
transaction: event.transaction ?? _window.location.pathname,
);
}

Expand Down
14 changes: 14 additions & 0 deletions dart/test/enricher/web_enricher_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
6 changes: 6 additions & 0 deletions flutter/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,12 @@ class SecondaryScaffold extends StatelessWidget {
Navigator.pop(context);
},
),
MaterialButton(
child: const Text('throw uncaught exception'),
onPressed: () {
throw Exception('Exception from SecondaryScaffold');
},
),
],
),
),
Expand Down
21 changes: 16 additions & 5 deletions flutter/lib/src/navigation/sentry_navigator_observer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<PageRoute<dynamic>> {
SentryNavigatorObserver({Hub? hub}) : hub = hub ?? HubAdapter();
SentryNavigatorObserver({Hub? hub, this.setTransaction = true})
: _hub = hub ?? HubAdapter();

final Hub hub;
final Hub _hub;
final bool setTransaction;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can make it private and probably give a better naming, also writing in the ctor docs that enabling this overwrites the current transaction name in the scope.

about the name, maybe routeNameAsTransactionName not sure, but setTransaction only is too subjective IMO

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can't be made private because it's a named constructor argument. Though because it's final it's basically readonly.


@override
void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) {
super.didPush(route, previousRoute);
setCurrentRoute(route.settings.name);
_addBreadcrumb(
type: 'didPush',
from: previousRoute?.settings,
Expand All @@ -52,7 +55,7 @@ class SentryNavigatorObserver extends RouteObserver<PageRoute<dynamic>> {
@override
void didReplace({Route<dynamic>? newRoute, Route<dynamic>? oldRoute}) {
super.didReplace(newRoute: newRoute, oldRoute: oldRoute);

setCurrentRoute(newRoute?.settings.name);
_addBreadcrumb(
type: 'didReplace',
from: oldRoute?.settings,
Expand All @@ -63,7 +66,7 @@ class SentryNavigatorObserver extends RouteObserver<PageRoute<dynamic>> {
@override
void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) {
super.didPop(route, previousRoute);

setCurrentRoute(previousRoute?.settings.name);
_addBreadcrumb(
type: 'didPop',
from: route.settings,
Expand All @@ -76,12 +79,20 @@ class SentryNavigatorObserver extends RouteObserver<PageRoute<dynamic>> {
RouteSettings? from,
RouteSettings? to,
}) {
hub.addBreadcrumb(RouteObserverBreadcrumb(
_hub.addBreadcrumb(RouteObserverBreadcrumb(
navigationType: type,
from: from,
to: to,
));
}

void setCurrentRoute(String? name) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What should happen if the route name is null?
Resetting the transaction name to null or just nothing?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nothing I'd say, but I guess the route is never null anyway

if (setTransaction) {
_hub.configureScope((scope) {
scope.transaction = name;
});
}
}
}

/// This class makes it easier to record breadcrumbs for events of Flutters
Expand Down
43 changes: 43 additions & 0 deletions flutter/test/sentry_navigator_observer_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -226,5 +227,47 @@ void main() {
).data,
);
});

test('route name as transaction', () {
final hub = _MockHub();
final observer = SentryNavigatorObserver(hub: hub, setTransaction: true);
Comment on lines +232 to +233
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can make a fixture


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);
}
}