Skip to content

Commit

Permalink
Merge branch 'main' into feat/replay-enhancements
Browse files Browse the repository at this point in the history
  • Loading branch information
vaind authored Sep 4, 2024
2 parents 86474fb + 3a16179 commit 77eb53a
Show file tree
Hide file tree
Showing 17 changed files with 424 additions and 16 deletions.
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,36 @@
);
```

- Support allowUrls and denyUrls for Flutter Web ([#2227](https://github.com/getsentry/sentry-dart/pull/2227))

```dart
await SentryFlutter.init(
(options) {
...
options.allowUrls = ["^https://sentry.com.*\$", "my-custom-domain"];
options.denyUrls = ["^.*ends-with-this\$", "denied-url"];
},
appRunner: () => runApp(MyApp()),
);
```

### Dependencies

- Bump Cocoa SDK from v8.35.1 to v8.36.0 ([#2252](https://github.com/getsentry/sentry-dart/pull/2252))
- [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#8360)
- [diff](https://github.com/getsentry/sentry-cocoa/compare/8.35.1...8.36.0)

### Fixes

- Only access renderObject if `hasSize` is true ([#2263](https://github.com/getsentry/sentry-dart/pull/2263))

## 8.8.0

### Features

- Add `SentryFlutter.nativeCrash()` using MethodChannels for Android and iOS ([#2239](https://github.com/getsentry/sentry-dart/pull/2239))
- This can be used to test if native crash reporting works

- Add `ignoreRoutes` parameter to `SentryNavigatorObserver`. ([#2218](https://github.com/getsentry/sentry-dart/pull/2218))
- This will ignore the Routes and prevent the Route from being pushed to the Sentry server.
- Ignored routes will also create no TTID and TTFD spans.
Expand Down
12 changes: 3 additions & 9 deletions dart/lib/src/sentry_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import 'transport/rate_limiter.dart';
import 'transport/spotlight_http_transport.dart';
import 'transport/task_queue.dart';
import 'utils/isolate_utils.dart';
import 'utils/regex_utils.dart';
import 'utils/stacktrace_utils.dart';
import 'version.dart';

Expand Down Expand Up @@ -196,7 +197,7 @@ class SentryClient {
}

var message = event.message!.formatted;
return _isMatchingRegexPattern(message, _options.ignoreErrors);
return isMatchingRegexPattern(message, _options.ignoreErrors);
}

SentryEvent _prepareEvent(SentryEvent event, {dynamic stackTrace}) {
Expand Down Expand Up @@ -415,7 +416,7 @@ class SentryClient {
}

var name = transaction.tracer.name;
return _isMatchingRegexPattern(name, _options.ignoreTransactions);
return isMatchingRegexPattern(name, _options.ignoreTransactions);
}

/// Reports the [envelope] to Sentry.io.
Expand Down Expand Up @@ -593,11 +594,4 @@ class SentryClient {
SentryId.empty(),
);
}

bool _isMatchingRegexPattern(String value, List<String> regexPattern,
{bool caseSensitive = false}) {
final combinedRegexPattern = regexPattern.join('|');
final regExp = RegExp(combinedRegexPattern, caseSensitive: caseSensitive);
return regExp.hasMatch(value);
}
}
2 changes: 2 additions & 0 deletions dart/lib/src/sentry_options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -186,10 +186,12 @@ class SentryOptions {

/// The ignoreErrors tells the SDK which errors should be not sent to the sentry server.
/// If an null or an empty list is used, the SDK will send all transactions.
/// To use regex add the `^` and the `$` to the string.
List<String> ignoreErrors = [];

/// The ignoreTransactions tells the SDK which transactions should be not sent to the sentry server.
/// If null or an empty list is used, the SDK will send all transactions.
/// To use regex add the `^` and the `$` to the string.
List<String> ignoreTransactions = [];

final List<String> _inAppExcludes = [];
Expand Down
4 changes: 0 additions & 4 deletions dart/lib/src/transport/spotlight_http_transport.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,6 @@ class SpotlightHttpTransport extends Transport {
Future<void> _sendToSpotlight(SentryEnvelope envelope) async {
envelope.header.sentAt = _options.clock();

// Screenshots do not work currently https://github.com/getsentry/spotlight/issues/274
envelope.items
.removeWhere((element) => element.header.contentType == 'image/png');

final spotlightRequest = await _requestHandler.createRequest(envelope);

final response = await _options.httpClient
Expand Down
9 changes: 9 additions & 0 deletions dart/lib/src/utils/regex_utils.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import 'package:meta/meta.dart';

@internal
bool isMatchingRegexPattern(String value, List<String> regexPattern,
{bool caseSensitive = false}) {
final combinedRegexPattern = regexPattern.join('|');
final regExp = RegExp(combinedRegexPattern, caseSensitive: caseSensitive);
return regExp.hasMatch(value);
}
24 changes: 24 additions & 0 deletions dart/test/utils/regex_utils_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import 'package:sentry/src/utils/regex_utils.dart';
import 'package:test/test.dart';

void main() {
group('regex_utils', () {
final testString = "this is a test";

test('testString contains string pattern', () {
expect(isMatchingRegexPattern(testString, ["is"]), isTrue);
});

test('testString does not contain string pattern', () {
expect(isMatchingRegexPattern(testString, ["not"]), isFalse);
});

test('testString contains regex pattern', () {
expect(isMatchingRegexPattern(testString, ["^this.*\$"]), isTrue);
});

test('testString does not contain regex pattern', () {
expect(isMatchingRegexPattern(testString, ["^is.*\$"]), isFalse);
});
});
}
2 changes: 1 addition & 1 deletion flutter/ios/Classes/SentryFlutterPluginApple.swift
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ public class SentryFlutterPluginApple: NSObject, FlutterPlugin {
case "nativeCrash":
crash()

case "sendReplayForEvent":
case "captureReplay":
#if canImport(UIKit) && !SENTRY_NO_UIKIT && (os(iOS) || os(tvOS))
PrivateSentrySDKOnly.captureReplay()
result(PrivateSentrySDKOnly.getReplayId())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import 'dart:html' as html show window, Window;

Check warning on line 1 in flutter/lib/src/event_processor/url_filter/html_url_filter_event_processor.dart

View workflow job for this annotation

GitHub Actions / analyze / analyze

Unused import: 'dart:html'.

Try removing the import directive. See https://dart.dev/diagnostics/unused_import to learn more about this problem.

import '../../../sentry_flutter.dart';
import 'url_filter_event_processor.dart';
// ignore: implementation_imports
import 'package:sentry/src/utils/regex_utils.dart';

// ignore_for_file: invalid_use_of_internal_member

UrlFilterEventProcessor urlFilterEventProcessor(SentryFlutterOptions options) =>
WebUrlFilterEventProcessor(options);

class WebUrlFilterEventProcessor implements UrlFilterEventProcessor {
WebUrlFilterEventProcessor(
this._options,
);

final SentryFlutterOptions _options;

@override
SentryEvent? apply(SentryEvent event, Hint hint) {
final frames = _getStacktraceFrames(event);
final lastPath = frames?.first?.absPath;

if (lastPath == null) {
return event;
}

if (_options.allowUrls.isNotEmpty &&
!isMatchingRegexPattern(lastPath, _options.allowUrls)) {
return null;
}

if (_options.denyUrls.isNotEmpty &&
isMatchingRegexPattern(lastPath, _options.denyUrls)) {
return null;
}

return event;
}

Iterable<SentryStackFrame?>? _getStacktraceFrames(SentryEvent event) {
if (event.exceptions?.isNotEmpty == true) {
return event.exceptions?.first.stackTrace?.frames;
}
if (event.threads?.isNotEmpty == true) {
final stacktraces = event.threads?.map((e) => e.stacktrace);
return stacktraces
?.where((element) => element != null)
.expand((element) => element!.frames);
}
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import '../../../sentry_flutter.dart';
import 'url_filter_event_processor.dart';

UrlFilterEventProcessor urlFilterEventProcessor(SentryFlutterOptions _) =>
IoUrlFilterEventProcessor();

class IoUrlFilterEventProcessor implements UrlFilterEventProcessor {
@override
SentryEvent apply(SentryEvent event, Hint hint) => event;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import '../../../sentry_flutter.dart';
import 'io_url_filter_event_processor.dart'
if (dart.library.html) 'html_url_filter_event_processor.dart'
if (dart.library.js_interop) 'web_url_filter_event_processor.dart';

abstract class UrlFilterEventProcessor implements EventProcessor {
factory UrlFilterEventProcessor(SentryFlutterOptions options) =>
urlFilterEventProcessor(options);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// We would lose compatibility with old dart versions by adding web to pubspec.
// ignore: depend_on_referenced_packages
import 'package:web/web.dart' as web show window, Window;

Check warning on line 3 in flutter/lib/src/event_processor/url_filter/web_url_filter_event_processor.dart

View workflow job for this annotation

GitHub Actions / analyze / analyze

Unused import: 'package:web/web.dart'.

Try removing the import directive. See https://dart.dev/diagnostics/unused_import to learn more about this problem.

import '../../../sentry_flutter.dart';
import 'url_filter_event_processor.dart';
// ignore: implementation_imports
import 'package:sentry/src/utils/regex_utils.dart';

// ignore_for_file: invalid_use_of_internal_member

UrlFilterEventProcessor urlFilterEventProcessor(SentryFlutterOptions options) =>
WebUrlFilterEventProcessor(options);

class WebUrlFilterEventProcessor implements UrlFilterEventProcessor {
WebUrlFilterEventProcessor(
this._options,
);

final SentryFlutterOptions _options;

@override
SentryEvent? apply(SentryEvent event, Hint hint) {
final frames = _getStacktraceFrames(event);
final lastPath = frames?.first?.absPath;

if (lastPath == null) {
return event;
}

if (_options.allowUrls.isNotEmpty &&
!isMatchingRegexPattern(lastPath, _options.allowUrls)) {
return null;
}

if (_options.denyUrls.isNotEmpty &&
isMatchingRegexPattern(lastPath, _options.denyUrls)) {
return null;
}

return event;
}

Iterable<SentryStackFrame?>? _getStacktraceFrames(SentryEvent event) {
if (event.exceptions?.isNotEmpty == true) {
return event.exceptions?.first.stackTrace?.frames;
}
if (event.threads?.isNotEmpty == true) {
final stacktraces = event.threads?.map((e) => e.stacktrace);
return stacktraces
?.where((element) => element != null)
.expand((element) => element!.frames);
}
return null;
}
}
2 changes: 2 additions & 0 deletions flutter/lib/src/sentry_flutter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import 'event_processor/android_platform_exception_event_processor.dart';
import 'event_processor/flutter_enricher_event_processor.dart';
import 'event_processor/flutter_exception_event_processor.dart';
import 'event_processor/platform_exception_event_processor.dart';
import 'event_processor/url_filter/url_filter_event_processor.dart';
import 'event_processor/widget_event_processor.dart';
import 'file_system_transport.dart';
import 'flutter_exception_type_identifier.dart';
Expand Down Expand Up @@ -131,6 +132,7 @@ mixin SentryFlutter {

options.addEventProcessor(FlutterEnricherEventProcessor(options));
options.addEventProcessor(WidgetEventProcessor());
options.addEventProcessor(UrlFilterEventProcessor(options));

if (options.platformChecker.platform.isAndroid) {
options.addEventProcessor(
Expand Down
15 changes: 15 additions & 0 deletions flutter/lib/src/sentry_flutter_options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,21 @@ class SentryFlutterOptions extends SentryOptions {
/// See https://api.flutter.dev/flutter/foundation/FlutterErrorDetails/silent.html
bool reportSilentFlutterErrors = false;

/// (Web only) Events only occurring on these Urls will be handled and sent to sentry.
/// If an empty list is used, the SDK will send all errors.
/// `allowUrls` uses regex for the matching.
///
/// If used on a platform other than Web, this setting will be ignored.
List<String> allowUrls = [];

/// (Web only) Events occurring on these Urls will be ignored and are not sent to sentry.
/// If an empty list is used, the SDK will send all errors.
/// `denyUrls` uses regex for the matching.
/// In combination with `allowUrls` you can block subdomains of the domains listed in `allowUrls`.
///
/// If used on a platform other than Web, this setting will be ignored.
List<String> denyUrls = [];

/// Enables Out of Memory Tracking for iOS and macCatalyst.
/// See the following link for more information and possible restrictions:
/// https://docs.sentry.io/platforms/apple/guides/ios/configuration/out-of-memory/
Expand Down
2 changes: 1 addition & 1 deletion flutter/lib/src/view_hierarchy/sentry_tree_walker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ class _TreeWalker {
double? alpha;

final renderObject = element.renderObject;
if (renderObject is RenderBox) {
if (renderObject is RenderBox && renderObject.hasSize) {
final offset = renderObject.localToGlobal(Offset.zero);
if (offset.dx > 0) {
x = offset.dx;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
@TestOn('vm')
library flutter_test;

import 'package:flutter_test/flutter_test.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:sentry_flutter/src/event_processor/url_filter/url_filter_event_processor.dart';

void main() {
group("ignore allowUrls and denyUrls for non Web", () {
late Fixture fixture;

setUp(() async {
fixture = Fixture();
});

test('returns the event and ignore allowUrls and denyUrls for non Web',
() async {
SentryEvent? event = SentryEvent(
request: SentryRequest(
url: 'another.url/for/a/special/test/testing/this-feature',
),
);
fixture.options.allowUrls = ["^this.is/.*\$"];
fixture.options.denyUrls = ["special"];

var eventProcessor = fixture.getSut();
event = await eventProcessor.apply(event, Hint());

expect(event, isNotNull);
});
});
}

class Fixture {
SentryFlutterOptions options = SentryFlutterOptions();
UrlFilterEventProcessor getSut() {
return UrlFilterEventProcessor(options);
}
}
Loading

0 comments on commit 77eb53a

Please sign in to comment.