Skip to content

Commit db933d5

Browse files
authored
Merge branch 'main' into feat/request-for-otel
2 parents 7b51dbd + 5c403ff commit db933d5

File tree

12 files changed

+247
-11
lines changed

12 files changed

+247
-11
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22

33
## Unreleased
44

5+
### Features
6+
7+
- Add `options.ignoredErrors` to filter out errors that match a certain String or Regex ([#4083](https://github.com/getsentry/sentry-java/pull/4083))
8+
- The matching is attempted on `event.message`, `event.formatted`, and `{event.throwable.class.name}: {event.throwable.message}`
9+
- Can be set in `sentry.properties`, e.g. `ignored-errors=Some error,Another .*`
10+
- Can be set in environment variables, e.g. `SENTRY_IGNORED_ERRORS=Some error,Another .*`
11+
- For Spring Boot, it can be set in `application.properties`, e.g. `sentry.ignored-errors=Some error,Another .*`
12+
513
### Fixes
614

715
- Avoid logging an error when a float is passed in the manifest ([#4031](https://github.com/getsentry/sentry-java/pull/4031))

sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentryAutoConfigurationTest.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ class SentryAutoConfigurationTest {
174174
"sentry.enabled=false",
175175
"sentry.send-modules=false",
176176
"sentry.ignored-checkins=slug1,slugB",
177+
"sentry.ignored-errors=Some error,Another .*",
177178
"sentry.ignored-transactions=transactionName1,transactionNameB",
178179
"sentry.enable-backpressure-handling=false",
179180
"sentry.enable-spotlight=true",
@@ -215,6 +216,7 @@ class SentryAutoConfigurationTest {
215216
assertThat(options.isEnabled).isEqualTo(false)
216217
assertThat(options.isSendModules).isEqualTo(false)
217218
assertThat(options.ignoredCheckIns).containsOnly(FilterString("slug1"), FilterString("slugB"))
219+
assertThat(options.ignoredErrors).containsOnly(FilterString("Some error"), FilterString("Another .*"))
218220
assertThat(options.ignoredTransactions).containsOnly(FilterString("transactionName1"), FilterString("transactionNameB"))
219221
assertThat(options.isEnableBackpressureHandling).isEqualTo(false)
220222
assertThat(options.isForceInit).isEqualTo(true)

sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentryAutoConfigurationTest.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ class SentryAutoConfigurationTest {
173173
"sentry.enabled=false",
174174
"sentry.send-modules=false",
175175
"sentry.ignored-checkins=slug1,slugB",
176+
"sentry.ignored-errors=Some error,Another .*",
176177
"sentry.ignored-transactions=transactionName1,transactionNameB",
177178
"sentry.enable-backpressure-handling=false",
178179
"sentry.enable-spotlight=true",
@@ -214,6 +215,7 @@ class SentryAutoConfigurationTest {
214215
assertThat(options.isEnabled).isEqualTo(false)
215216
assertThat(options.isSendModules).isEqualTo(false)
216217
assertThat(options.ignoredCheckIns).containsOnly(FilterString("slug1"), FilterString("slugB"))
218+
assertThat(options.ignoredErrors).containsOnly(FilterString("Some error"), FilterString("Another .*"))
217219
assertThat(options.ignoredTransactions).containsOnly(FilterString("transactionName1"), FilterString("transactionNameB"))
218220
assertThat(options.isEnableBackpressureHandling).isEqualTo(false)
219221
assertThat(options.isForceInit).isEqualTo(true)

sentry/api/sentry.api

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,7 @@ public final class io/sentry/ExternalOptions {
452452
public fun getEnvironment ()Ljava/lang/String;
453453
public fun getIdleTimeout ()Ljava/lang/Long;
454454
public fun getIgnoredCheckIns ()Ljava/util/List;
455+
public fun getIgnoredErrors ()Ljava/util/List;
455456
public fun getIgnoredExceptionsForType ()Ljava/util/Set;
456457
public fun getIgnoredTransactions ()Ljava/util/List;
457458
public fun getInAppExcludes ()Ljava/util/List;
@@ -491,6 +492,7 @@ public final class io/sentry/ExternalOptions {
491492
public fun setGlobalHubMode (Ljava/lang/Boolean;)V
492493
public fun setIdleTimeout (Ljava/lang/Long;)V
493494
public fun setIgnoredCheckIns (Ljava/util/List;)V
495+
public fun setIgnoredErrors (Ljava/util/List;)V
494496
public fun setIgnoredTransactions (Ljava/util/List;)V
495497
public fun setMaxRequestBodySize (Lio/sentry/SentryOptions$RequestSize;)V
496498
public fun setPrintUncaughtStackTrace (Ljava/lang/Boolean;)V
@@ -2814,6 +2816,7 @@ public class io/sentry/SentryOptions {
28142816
public fun addContextTag (Ljava/lang/String;)V
28152817
public fun addEventProcessor (Lio/sentry/EventProcessor;)V
28162818
public fun addIgnoredCheckIn (Ljava/lang/String;)V
2819+
public fun addIgnoredError (Ljava/lang/String;)V
28172820
public fun addIgnoredExceptionForType (Ljava/lang/Class;)V
28182821
public fun addIgnoredSpanOrigin (Ljava/lang/String;)V
28192822
public fun addIgnoredTransaction (Ljava/lang/String;)V
@@ -2855,6 +2858,7 @@ public class io/sentry/SentryOptions {
28552858
public fun getGestureTargetLocators ()Ljava/util/List;
28562859
public fun getIdleTimeout ()Ljava/lang/Long;
28572860
public fun getIgnoredCheckIns ()Ljava/util/List;
2861+
public fun getIgnoredErrors ()Ljava/util/List;
28582862
public fun getIgnoredExceptionsForType ()Ljava/util/Set;
28592863
public fun getIgnoredSpanOrigins ()Ljava/util/List;
28602864
public fun getIgnoredTransactions ()Ljava/util/List;
@@ -2987,6 +2991,7 @@ public class io/sentry/SentryOptions {
29872991
public fun setGlobalHubMode (Ljava/lang/Boolean;)V
29882992
public fun setIdleTimeout (Ljava/lang/Long;)V
29892993
public fun setIgnoredCheckIns (Ljava/util/List;)V
2994+
public fun setIgnoredErrors (Ljava/util/List;)V
29902995
public fun setIgnoredSpanOrigins (Ljava/util/List;)V
29912996
public fun setIgnoredTransactions (Ljava/util/List;)V
29922997
public fun setInitPriority (Lio/sentry/InitPriority;)V
@@ -6047,6 +6052,11 @@ public final class io/sentry/util/DebugMetaPropertiesApplier {
60476052
public static fun getProguardUuid (Ljava/util/Properties;)Ljava/lang/String;
60486053
}
60496054

6055+
public final class io/sentry/util/ErrorUtils {
6056+
public fun <init> ()V
6057+
public static fun isIgnored (Ljava/util/List;Lio/sentry/SentryEvent;)Z
6058+
}
6059+
60506060
public final class io/sentry/util/EventProcessorUtils {
60516061
public fun <init> ()V
60526062
public static fun unwrap (Ljava/util/List;)Ljava/util/List;
@@ -6055,6 +6065,7 @@ public final class io/sentry/util/EventProcessorUtils {
60556065
public final class io/sentry/util/ExceptionUtils {
60566066
public fun <init> ()V
60576067
public static fun findRootCause (Ljava/lang/Throwable;)Ljava/lang/Throwable;
6068+
public static fun isIgnored (Ljava/util/Set;Ljava/lang/Throwable;)Z
60586069
}
60596070

60606071
public final class io/sentry/util/FileUtils {

sentry/src/main/java/io/sentry/ExternalOptions.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
package io.sentry;
22

33
import io.sentry.config.PropertiesProvider;
4-
import java.util.List;
5-
import java.util.Locale;
6-
import java.util.Map;
7-
import java.util.Set;
4+
import java.util.*;
85
import java.util.concurrent.ConcurrentHashMap;
96
import java.util.concurrent.CopyOnWriteArrayList;
107
import java.util.concurrent.CopyOnWriteArraySet;
@@ -39,6 +36,7 @@ public final class ExternalOptions {
3936
private @Nullable Long idleTimeout;
4037
private final @NotNull Set<Class<? extends Throwable>> ignoredExceptionsForType =
4138
new CopyOnWriteArraySet<>();
39+
private @Nullable List<String> ignoredErrors;
4240
private @Nullable Boolean printUncaughtStackTrace;
4341
private @Nullable Boolean sendClientReports;
4442
private @NotNull Set<String> bundleIds = new CopyOnWriteArraySet<>();
@@ -130,6 +128,8 @@ public final class ExternalOptions {
130128
}
131129
options.setIdleTimeout(propertiesProvider.getLongProperty("idle-timeout"));
132130

131+
options.setIgnoredErrors(propertiesProvider.getList("ignored-errors"));
132+
133133
options.setEnabled(propertiesProvider.getBooleanProperty("enabled"));
134134

135135
options.setEnablePrettySerializationOutput(
@@ -373,6 +373,14 @@ public void setIdleTimeout(final @Nullable Long idleTimeout) {
373373
this.idleTimeout = idleTimeout;
374374
}
375375

376+
public @Nullable List<String> getIgnoredErrors() {
377+
return ignoredErrors;
378+
}
379+
380+
public void setIgnoredErrors(final @Nullable List<String> ignoredErrors) {
381+
this.ignoredErrors = ignoredErrors;
382+
}
383+
376384
public @Nullable Boolean getSendClientReports() {
377385
return sendClientReports;
378386
}

sentry/src/main/java/io/sentry/SentryClient.java

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,7 @@
1111
import io.sentry.protocol.SentryTransaction;
1212
import io.sentry.transport.ITransport;
1313
import io.sentry.transport.RateLimiter;
14-
import io.sentry.util.CheckInUtils;
15-
import io.sentry.util.HintUtils;
16-
import io.sentry.util.Objects;
17-
import io.sentry.util.Random;
18-
import io.sentry.util.SentryRandom;
19-
import io.sentry.util.TracingUtils;
14+
import io.sentry.util.*;
2015
import java.io.Closeable;
2116
import java.io.IOException;
2217
import java.util.ArrayList;
@@ -103,7 +98,8 @@ private boolean shouldApplyScopeData(final @NotNull CheckIn event, final @NotNul
10398

10499
if (event != null) {
105100
final Throwable eventThrowable = event.getThrowable();
106-
if (eventThrowable != null && options.containsIgnoredExceptionForType(eventThrowable)) {
101+
if (eventThrowable != null
102+
&& ExceptionUtils.isIgnored(options.getIgnoredExceptionsForType(), eventThrowable)) {
107103
options
108104
.getLogger()
109105
.log(
@@ -115,6 +111,19 @@ private boolean shouldApplyScopeData(final @NotNull CheckIn event, final @NotNul
115111
.recordLostEvent(DiscardReason.EVENT_PROCESSOR, DataCategory.Error);
116112
return SentryId.EMPTY_ID;
117113
}
114+
115+
if (ErrorUtils.isIgnored(options.getIgnoredErrors(), event)) {
116+
options
117+
.getLogger()
118+
.log(
119+
SentryLevel.DEBUG,
120+
"Event was dropped as it matched a string/pattern in ignoredErrors",
121+
event.getMessage());
122+
options
123+
.getClientReportRecorder()
124+
.recordLostEvent(DiscardReason.EVENT_PROCESSOR, DataCategory.Error);
125+
return SentryId.EMPTY_ID;
126+
}
118127
}
119128

120129
if (shouldApplyScopeData(event, hint)) {

sentry/src/main/java/io/sentry/SentryOptions.java

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ public class SentryOptions {
6969
private final @NotNull Set<Class<? extends Throwable>> ignoredExceptionsForType =
7070
new CopyOnWriteArraySet<>();
7171

72+
/**
73+
* Strings or regex patterns that possible error messages for an event will be tested against. If
74+
* there is a match, the captured event will not be sent to Sentry.
75+
*/
76+
private @Nullable List<FilterString> ignoredErrors = null;
77+
7278
/**
7379
* Code that provides middlewares, bindings or hooks into certain frameworks or environments,
7480
* along with code that inserts those bindings and activates them.
@@ -1572,6 +1578,55 @@ boolean containsIgnoredExceptionForType(final @NotNull Throwable throwable) {
15721578
return this.ignoredExceptionsForType.contains(throwable.getClass());
15731579
}
15741580

1581+
/**
1582+
* Returns the list of strings/regex patterns that `event.message`, `event.formatted`, and
1583+
* `{event.throwable.class.name}: {event.throwable.message}` are checked against to determine if
1584+
* an event shall be sent to Sentry or ignored.
1585+
*
1586+
* @return the list of strings/regex patterns that `event.message`, `event.formatted`, and
1587+
* `{event.throwable.class.name}: {event.throwable.message}` are checked against to determine
1588+
* if an event shall be sent to Sentry or ignored
1589+
*/
1590+
public @Nullable List<FilterString> getIgnoredErrors() {
1591+
return ignoredErrors;
1592+
}
1593+
1594+
/**
1595+
* Sets the list of strings/regex patterns that `event.message`, `event.formatted`, and
1596+
* `{event.throwable.class.name}: {event.throwable.message}` are checked against to determine if
1597+
* an event shall be sent to Sentry or ignored.
1598+
*
1599+
* @param ignoredErrors the list of strings/regex patterns
1600+
*/
1601+
public void setIgnoredErrors(final @Nullable List<String> ignoredErrors) {
1602+
if (ignoredErrors == null) {
1603+
this.ignoredErrors = null;
1604+
} else {
1605+
@NotNull final List<FilterString> patterns = new ArrayList<>();
1606+
for (String pattern : ignoredErrors) {
1607+
if (pattern != null && !pattern.isEmpty()) {
1608+
patterns.add(new FilterString(pattern));
1609+
}
1610+
}
1611+
1612+
this.ignoredErrors = patterns;
1613+
}
1614+
}
1615+
1616+
/**
1617+
* Adds an item to the list of strings/regex patterns that `event.message`, `event.formatted`, and
1618+
* `{event.throwable.class.name}: {event.throwable.message}` are checked against to determine if
1619+
* an event shall be sent to Sentry or ignored.
1620+
*
1621+
* @param pattern the string/regex pattern
1622+
*/
1623+
public void addIgnoredError(final @NotNull String pattern) {
1624+
if (ignoredErrors == null) {
1625+
ignoredErrors = new ArrayList<>();
1626+
}
1627+
ignoredErrors.add(new FilterString(pattern));
1628+
}
1629+
15751630
/**
15761631
* Returns the maximum number of spans that can be attached to single transaction.
15771632
*
@@ -2801,6 +2856,10 @@ public void merge(final @NotNull ExternalOptions options) {
28012856
final List<String> ignoredTransactions = new ArrayList<>(options.getIgnoredTransactions());
28022857
setIgnoredTransactions(ignoredTransactions);
28032858
}
2859+
if (options.getIgnoredErrors() != null) {
2860+
final List<String> ignoredExceptions = new ArrayList<>(options.getIgnoredErrors());
2861+
setIgnoredErrors(ignoredExceptions);
2862+
}
28042863
if (options.isEnableBackpressureHandling() != null) {
28052864
setEnableBackpressureHandling(options.isEnableBackpressureHandling());
28062865
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package io.sentry.util;
2+
3+
import io.sentry.FilterString;
4+
import io.sentry.SentryEvent;
5+
import io.sentry.protocol.Message;
6+
import java.util.HashSet;
7+
import java.util.List;
8+
import java.util.Set;
9+
import org.jetbrains.annotations.ApiStatus;
10+
import org.jetbrains.annotations.NotNull;
11+
import org.jetbrains.annotations.Nullable;
12+
13+
public final class ErrorUtils {
14+
15+
/** Checks if an error has been ignored. */
16+
@ApiStatus.Internal
17+
public static boolean isIgnored(
18+
final @Nullable List<FilterString> ignoredErrors, final @NotNull SentryEvent event) {
19+
if (event == null || ignoredErrors == null || ignoredErrors.isEmpty()) {
20+
return false;
21+
}
22+
23+
final @NotNull Set<String> possibleMessages = new HashSet<>();
24+
25+
final @Nullable Message eventMessage = event.getMessage();
26+
if (eventMessage != null) {
27+
final @Nullable String stringMessage = eventMessage.getMessage();
28+
if (stringMessage != null) {
29+
possibleMessages.add(stringMessage);
30+
}
31+
final @Nullable String formattedMessage = eventMessage.getFormatted();
32+
if (formattedMessage != null) {
33+
possibleMessages.add(formattedMessage);
34+
}
35+
}
36+
final @Nullable Throwable throwable = event.getThrowable();
37+
if (throwable != null) {
38+
possibleMessages.add(throwable.toString());
39+
}
40+
41+
for (final @NotNull FilterString filter : ignoredErrors) {
42+
if (possibleMessages.contains(filter.getFilterString())) {
43+
return true;
44+
}
45+
}
46+
47+
for (final @NotNull FilterString filter : ignoredErrors) {
48+
for (final @NotNull String message : possibleMessages) {
49+
if (filter.matches(message)) {
50+
return true;
51+
}
52+
}
53+
}
54+
55+
return false;
56+
}
57+
}

sentry/src/main/java/io/sentry/util/ExceptionUtils.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.sentry.util;
22

3+
import java.util.Set;
34
import org.jetbrains.annotations.ApiStatus;
45
import org.jetbrains.annotations.NotNull;
56

@@ -20,4 +21,12 @@ public final class ExceptionUtils {
2021
}
2122
return rootCause;
2223
}
24+
25+
/** Checks if an exception has been ignored. */
26+
@ApiStatus.Internal
27+
public static boolean isIgnored(
28+
final @NotNull Set<Class<? extends Throwable>> ignoredExceptionsForType,
29+
final @NotNull Throwable throwable) {
30+
return ignoredExceptionsForType.contains(throwable.getClass());
31+
}
2332
}

sentry/src/test/java/io/sentry/ExternalOptionsTest.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,15 @@ class ExternalOptionsTest {
209209
}
210210
}
211211

212+
@Test
213+
fun `creates options with ignored error patterns using external properties`() {
214+
val logger = mock<ILogger>()
215+
withPropertiesFile("ignored-errors=Some error,Another .*", logger) { options ->
216+
assertTrue(options.ignoredErrors!!.contains("Some error"))
217+
assertTrue(options.ignoredErrors!!.contains("Another .*"))
218+
}
219+
}
220+
212221
@Test
213222
fun `creates options with single bundle ID using external properties`() {
214223
withPropertiesFile("bundle-ids=12ea7a02-46ac-44c0-a5bb-6d1fd9586411") { options ->

0 commit comments

Comments
 (0)