Skip to content

Commit

Permalink
feat: add ignoreTransactions and ignoreErrors` #1391 (#2207)
Browse files Browse the repository at this point in the history
* Add ignoreTransactions and ignoreErrors #1391

* chore: add changelog entry

* move methods from sentry_options to sentry_client and change to private

* change discard reason to ignored

Co-authored-by: Giancarlo Buenaflor <giancarlobuenaflor97@gmail.com>

* change iterable to list

* add event recorder to ignoredTransactions

* add tests for ignoreTransactions

* set ignoreErrors list to empty list a default

Co-authored-by: Giancarlo Buenaflor <giancarlobuenaflor97@gmail.com>

* change variables to final for ignoreTransaction

Co-authored-by: Giancarlo Buenaflor <giancarlobuenaflor97@gmail.com>

* change var to final for ignoreErrors and adapt test

* Update CHANGELOG.md

Co-authored-by: Giancarlo Buenaflor <giancarlobuenaflor97@gmail.com>

* Add example for ignoreTransactions and ignoreErrors to changelog

* fix: check for empty ignoreError and ignoreTransaction before handling regex

* moved ignoreTransactions and ignoreErrors back to unreleased area in CHANGELOG.md

* refactored implementation of ignoreErrors and ignoreTransactions and improved test cases

* removed unnecessary backslash from tests

---------

Co-authored-by: Martin <>
Co-authored-by: Martin <martin@haintz.dev>
Co-authored-by: Giancarlo Buenaflor <giancarlobuenaflor97@gmail.com>
  • Loading branch information
3 people authored Aug 6, 2024
1 parent 7ec9238 commit 2e1e4ae
Show file tree
Hide file tree
Showing 4 changed files with 190 additions and 0 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,18 @@
### Features

- Add support for span level measurements. ([#2214](https://github.com/getsentry/sentry-dart/pull/2214))
- Add `ignoreTransactions` and `ignoreErrors` to options ([#2207](https://github.com/getsentry/sentry-dart/pull/2207))
```dart
await SentryFlutter.init(
(options) {
options.dsn = 'https://examplePublicKey@o0.ingest.sentry.io/0';
options.ignoreErrors = ["my-error", "^error-.*\$"];
options.ignoreTransactions = ["my-transaction", "^transaction-.*\$"];
...
},
appRunner: () => runApp(MyApp()),
);
```

## 8.6.0

Expand Down
46 changes: 46 additions & 0 deletions dart/lib/src/sentry_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,16 @@ class SentryClient {
dynamic stackTrace,
Hint? hint,
}) async {
if (_isIgnoredError(event)) {
_options.logger(
SentryLevel.debug,
'Error was ignored as specified in the ignoredErrors options.',
);
_options.recorder
.recordLostEvent(DiscardReason.ignored, _getCategory(event));
return _emptySentryId;
}

if (_options.containsIgnoredExceptionForType(event.throwable)) {
_options.logger(
SentryLevel.debug,
Expand Down Expand Up @@ -180,6 +190,15 @@ class SentryClient {
return id ?? SentryId.empty();
}

bool _isIgnoredError(SentryEvent event) {
if (event.message == null || _options.ignoreErrors.isEmpty) {
return false;
}

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

SentryEvent _prepareEvent(SentryEvent event, {dynamic stackTrace}) {
event = event.copyWith(
serverName: event.serverName ?? _options.serverName,
Expand Down Expand Up @@ -351,6 +370,17 @@ class SentryClient {
return _emptySentryId;
}

if (_isIgnoredTransaction(preparedTransaction)) {
_options.logger(
SentryLevel.debug,
'Transaction was ignored as specified in the ignoredTransactions options.',
);

_options.recorder.recordLostEvent(
DiscardReason.ignored, _getCategory(preparedTransaction));
return _emptySentryId;
}

preparedTransaction =
await _runBeforeSend(preparedTransaction, hint) as SentryTransaction?;

Expand Down Expand Up @@ -379,6 +409,15 @@ class SentryClient {
return id ?? SentryId.empty();
}

bool _isIgnoredTransaction(SentryTransaction transaction) {
if (_options.ignoreTransactions.isEmpty) {
return false;
}

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

/// Reports the [envelope] to Sentry.io.
Future<SentryId?> captureEnvelope(SentryEnvelope envelope) {
return _attachClientReportsAndSend(envelope);
Expand Down Expand Up @@ -554,4 +593,11 @@ 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);
}
}
8 changes: 8 additions & 0 deletions dart/lib/src/sentry_options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,14 @@ class SentryOptions {
/// sent. Events are picked randomly. Default is null (disabled)
double? sampleRate;

/// 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.
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.
List<String> ignoreTransactions = [];

final List<String> _inAppExcludes = [];

/// A list of string prefixes of packages names that do not belong to the app, but rather third-party
Expand Down
124 changes: 124 additions & 0 deletions dart/test/sentry_client_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1033,6 +1033,130 @@ void main() {
});
});

group('SentryClient ignored errors', () {
late Fixture fixture;

setUp(() {
fixture = Fixture();
fixture.options.ignoreErrors = ["my-error", "^error-.*\$"];
});

test('drop event if error message fully matches ignoreErrors value',
() async {
final event = SentryEvent(message: SentryMessage("my-error"));

final client = fixture.getSut();
await client.captureEvent(event);

expect((fixture.transport).called(0), true);
});

test('drop event if error message partially matches ignoreErrors value',
() async {
final event = SentryEvent(message: SentryMessage("this is my-error-foo"));

final client = fixture.getSut();
await client.captureEvent(event);

expect((fixture.transport).called(0), true);
});

test(
'drop event if error message partially matches ignoreErrors regex value',
() async {
final event = SentryEvent(message: SentryMessage("error-test message"));

final client = fixture.getSut();
await client.captureEvent(event);

expect((fixture.transport).called(0), true);
});

test('send event if error message does not match ignoreErrors value',
() async {
final event = SentryEvent(message: SentryMessage("warning"));

final client = fixture.getSut();
await client.captureEvent(event);

expect((fixture.transport).called(1), true);
});

test('send event if no values are set for ignoreErrors', () async {
fixture.options.ignoreErrors = [];
final event = SentryEvent(message: SentryMessage("this is a test event"));

final client = fixture.getSut();
await client.captureEvent(event);

expect((fixture.transport).called(1), true);
});
});

group('SentryClient ignored transactions', () {
late Fixture fixture;

setUp(() {
fixture = Fixture();
fixture.options.ignoreTransactions = [
"my-transaction",
"^transaction-.*\$"
];
});

test('drop transaction if name fully matches ignoreTransaction value',
() async {
final client = fixture.getSut();
final fakeTransaction = fixture.fakeTransaction();
fakeTransaction.tracer.name = "my-transaction";
await client.captureTransaction(fakeTransaction);

expect((fixture.transport).called(0), true);
});

test('drop transaction if name partially matches ignoreTransaction value',
() async {
final client = fixture.getSut();
final fakeTransaction = fixture.fakeTransaction();
fakeTransaction.tracer.name = "this is a transaction-test";
await client.captureTransaction(fakeTransaction);

expect((fixture.transport).called(0), true);
});

test(
'drop transaction if name partially matches ignoreTransaction regex value',
() async {
final client = fixture.getSut();
final fakeTransaction = fixture.fakeTransaction();
fakeTransaction.tracer.name = "transaction-test message";
await client.captureTransaction(fakeTransaction);

expect((fixture.transport).called(0), true);
});

test('send transaction if name does not match ignoreTransaction value',
() async {
final client = fixture.getSut();
final fakeTransaction = fixture.fakeTransaction();
fakeTransaction.tracer.name = "capture";
await client.captureTransaction(fakeTransaction);

expect((fixture.transport).called(1), true);
});

test('send transaction if no values are set for ignoreTransaction',
() async {
fixture.options.ignoreTransactions = [];
final client = fixture.getSut();
final fakeTransaction = fixture.fakeTransaction();
fakeTransaction.tracer.name = "this is a test transaction";
await client.captureTransaction(fakeTransaction);

expect((fixture.transport).called(1), true);
});
});

group('SentryClient ignored exceptions', () {
late Fixture fixture;

Expand Down

0 comments on commit 2e1e4ae

Please sign in to comment.