Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add options.ignoredErrors accepting String and Regex #4083

Merged
merged 8 commits into from
Jan 24, 2025
8 changes: 4 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@

### Features

- Add `options.ignoreExceptions` to filter out exceptions that match a certain String or Regex ([#4083](https://github.com/getsentry/sentry-java/pull/4083))
- Can be set in `sentry.properties`, e.g. `ignored-exceptions=java.lang.RuntimeException,io.sentry..*`
- Can be set in environment variables, e.g. `SENTRY_IGNORED_EXCEPTIONS=java.lang.RuntimeException,io.sentry..*`
- For Spring Boot, it can be set in `application.properties`, e.g. `sentry.ignored-exceptions=java.lang.RuntimeException,io.sentry..*`
- Add `options.ignoredErrors` to filter out errors that match a certain String or Regex ([#4083](https://github.com/getsentry/sentry-java/pull/4083))
lcian marked this conversation as resolved.
Show resolved Hide resolved
- Can be set in `sentry.properties`, e.g. `ignored-errors=Some error,Another .*`
- Can be set in environment variables, e.g. `SENTRY_IGNORED_ERRORS=Some error,Another .*`
- For Spring Boot, it can be set in `application.properties`, e.g. `sentry.ignored-errors=Some error,Another .*`

## 8.0.0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ class SentryAutoConfigurationTest {
"sentry.enabled=false",
"sentry.send-modules=false",
"sentry.ignored-checkins=slug1,slugB",
"sentry.ignored-exceptions=com.some.Exception,io.sentry..*",
"sentry.ignored-errors=Some error,Another .*",
"sentry.ignored-transactions=transactionName1,transactionNameB",
"sentry.enable-backpressure-handling=false",
"sentry.enable-spotlight=true",
Expand Down Expand Up @@ -216,7 +216,7 @@ class SentryAutoConfigurationTest {
assertThat(options.isEnabled).isEqualTo(false)
assertThat(options.isSendModules).isEqualTo(false)
assertThat(options.ignoredCheckIns).containsOnly(FilterString("slug1"), FilterString("slugB"))
assertThat(options.ignoredExceptions).containsOnly(FilterString("com.some.Exception"), FilterString("io.sentry..*"))
assertThat(options.ignoredErrors).containsOnly(FilterString("Some error"), FilterString("Another .*"))
assertThat(options.ignoredTransactions).containsOnly(FilterString("transactionName1"), FilterString("transactionNameB"))
assertThat(options.isEnableBackpressureHandling).isEqualTo(false)
assertThat(options.isForceInit).isEqualTo(true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ class SentryAutoConfigurationTest {
"sentry.enabled=false",
"sentry.send-modules=false",
"sentry.ignored-checkins=slug1,slugB",
"sentry.ignored-exceptions=com.some.Exception,io.sentry..*",
"sentry.ignored-errors=Some error,Another .*",
"sentry.ignored-transactions=transactionName1,transactionNameB",
"sentry.enable-backpressure-handling=false",
"sentry.enable-spotlight=true",
Expand Down Expand Up @@ -215,7 +215,7 @@ class SentryAutoConfigurationTest {
assertThat(options.isEnabled).isEqualTo(false)
assertThat(options.isSendModules).isEqualTo(false)
assertThat(options.ignoredCheckIns).containsOnly(FilterString("slug1"), FilterString("slugB"))
assertThat(options.ignoredExceptions).containsOnly(FilterString("com.some.Exception"), FilterString("io.sentry..*"))
assertThat(options.ignoredErrors).containsOnly(FilterString("Some error"), FilterString("Another .*"))
assertThat(options.ignoredTransactions).containsOnly(FilterString("transactionName1"), FilterString("transactionNameB"))
assertThat(options.isEnableBackpressureHandling).isEqualTo(false)
assertThat(options.isForceInit).isEqualTo(true)
Expand Down
18 changes: 11 additions & 7 deletions sentry/api/sentry.api
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,6 @@ public final class io/sentry/ExternalOptions {
public fun <init> ()V
public fun addBundleId (Ljava/lang/String;)V
public fun addContextTag (Ljava/lang/String;)V
public fun addIgnoredException (Ljava/lang/String;)V
public fun addIgnoredExceptionForType (Ljava/lang/Class;)V
public fun addInAppExclude (Ljava/lang/String;)V
public fun addInAppInclude (Ljava/lang/String;)V
Expand All @@ -453,7 +452,7 @@ public final class io/sentry/ExternalOptions {
public fun getEnvironment ()Ljava/lang/String;
public fun getIdleTimeout ()Ljava/lang/Long;
public fun getIgnoredCheckIns ()Ljava/util/List;
public fun getIgnoredExceptions ()Ljava/util/List;
public fun getIgnoredErrors ()Ljava/util/List;
public fun getIgnoredExceptionsForType ()Ljava/util/Set;
public fun getIgnoredTransactions ()Ljava/util/List;
public fun getInAppExcludes ()Ljava/util/List;
Expand Down Expand Up @@ -493,7 +492,7 @@ public final class io/sentry/ExternalOptions {
public fun setGlobalHubMode (Ljava/lang/Boolean;)V
public fun setIdleTimeout (Ljava/lang/Long;)V
public fun setIgnoredCheckIns (Ljava/util/List;)V
public fun setIgnoredExceptions (Ljava/util/List;)V
public fun setIgnoredErrors (Ljava/util/List;)V
public fun setIgnoredTransactions (Ljava/util/List;)V
public fun setMaxRequestBodySize (Lio/sentry/SentryOptions$RequestSize;)V
public fun setPrintUncaughtStackTrace (Ljava/lang/Boolean;)V
Expand Down Expand Up @@ -2817,7 +2816,7 @@ public class io/sentry/SentryOptions {
public fun addContextTag (Ljava/lang/String;)V
public fun addEventProcessor (Lio/sentry/EventProcessor;)V
public fun addIgnoredCheckIn (Ljava/lang/String;)V
public fun addIgnoredException (Ljava/lang/String;)V
public fun addIgnoredError (Ljava/lang/String;)V
public fun addIgnoredExceptionForType (Ljava/lang/Class;)V
public fun addIgnoredSpanOrigin (Ljava/lang/String;)V
public fun addIgnoredTransaction (Ljava/lang/String;)V
Expand Down Expand Up @@ -2859,7 +2858,7 @@ public class io/sentry/SentryOptions {
public fun getGestureTargetLocators ()Ljava/util/List;
public fun getIdleTimeout ()Ljava/lang/Long;
public fun getIgnoredCheckIns ()Ljava/util/List;
public fun getIgnoredExceptions ()Ljava/util/List;
public fun getIgnoredErrors ()Ljava/util/List;
public fun getIgnoredExceptionsForType ()Ljava/util/Set;
public fun getIgnoredSpanOrigins ()Ljava/util/List;
public fun getIgnoredTransactions ()Ljava/util/List;
Expand Down Expand Up @@ -2992,7 +2991,7 @@ public class io/sentry/SentryOptions {
public fun setGlobalHubMode (Ljava/lang/Boolean;)V
public fun setIdleTimeout (Ljava/lang/Long;)V
public fun setIgnoredCheckIns (Ljava/util/List;)V
public fun setIgnoredExceptions (Ljava/util/List;)V
public fun setIgnoredErrors (Ljava/util/List;)V
public fun setIgnoredSpanOrigins (Ljava/util/List;)V
public fun setIgnoredTransactions (Ljava/util/List;)V
public fun setInitPriority (Lio/sentry/InitPriority;)V
Expand Down Expand Up @@ -6053,6 +6052,11 @@ public final class io/sentry/util/DebugMetaPropertiesApplier {
public static fun getProguardUuid (Ljava/util/Properties;)Ljava/lang/String;
}

public final class io/sentry/util/ErrorUtils {
public fun <init> ()V
public static fun isIgnored (Ljava/util/List;Lio/sentry/SentryEvent;)Z
}

public final class io/sentry/util/EventProcessorUtils {
public fun <init> ()V
public static fun unwrap (Ljava/util/List;)Ljava/util/List;
Expand All @@ -6061,7 +6065,7 @@ public final class io/sentry/util/EventProcessorUtils {
public final class io/sentry/util/ExceptionUtils {
public fun <init> ()V
public static fun findRootCause (Ljava/lang/Throwable;)Ljava/lang/Throwable;
public static fun isIgnored (Ljava/util/Set;Ljava/util/List;Ljava/lang/Throwable;)Z
public static fun isIgnored (Ljava/util/Set;Ljava/lang/Throwable;)Z
}

public final class io/sentry/util/FileUtils {
Expand Down
19 changes: 6 additions & 13 deletions sentry/src/main/java/io/sentry/ExternalOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public final class ExternalOptions {
private @Nullable Long idleTimeout;
private final @NotNull Set<Class<? extends Throwable>> ignoredExceptionsForType =
new CopyOnWriteArraySet<>();
private @Nullable List<String> ignoredExceptions;
private @Nullable List<String> ignoredErrors;
private @Nullable Boolean printUncaughtStackTrace;
private @Nullable Boolean sendClientReports;
private @NotNull Set<String> bundleIds = new CopyOnWriteArraySet<>();
Expand Down Expand Up @@ -128,7 +128,7 @@ public final class ExternalOptions {
}
options.setIdleTimeout(propertiesProvider.getLongProperty("idle-timeout"));

options.setIgnoredExceptions(propertiesProvider.getList("ignored-exceptions"));
options.setIgnoredErrors(propertiesProvider.getList("ignored-errors"));

options.setEnabled(propertiesProvider.getBooleanProperty("enabled"));

Expand Down Expand Up @@ -373,19 +373,12 @@ public void setIdleTimeout(final @Nullable Long idleTimeout) {
this.idleTimeout = idleTimeout;
}

public @Nullable List<String> getIgnoredExceptions() {
return ignoredExceptions;
public @Nullable List<String> getIgnoredErrors() {
return ignoredErrors;
}

public void setIgnoredExceptions(final @Nullable List<String> ignoredExceptions) {
this.ignoredExceptions = ignoredExceptions;
}

public void addIgnoredException(final @NotNull String pattern) {
if (ignoredExceptions == null) {
ignoredExceptions = new ArrayList<>();
}
ignoredExceptions.add(pattern);
public void setIgnoredErrors(final @Nullable List<String> ignoredErrors) {
this.ignoredErrors = ignoredErrors;
}

public @Nullable Boolean getSendClientReports() {
Expand Down
18 changes: 14 additions & 4 deletions sentry/src/main/java/io/sentry/SentryClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,7 @@ private boolean shouldApplyScopeData(final @NotNull CheckIn event, final @NotNul
if (event != null) {
final Throwable eventThrowable = event.getThrowable();
if (eventThrowable != null
&& ExceptionUtils.isIgnored(
options.getIgnoredExceptionsForType(),
options.getIgnoredExceptions(),
eventThrowable)) {
&& ExceptionUtils.isIgnored(options.getIgnoredExceptionsForType(), eventThrowable)) {
options
.getLogger()
.log(
Expand All @@ -114,6 +111,19 @@ private boolean shouldApplyScopeData(final @NotNull CheckIn event, final @NotNul
.recordLostEvent(DiscardReason.EVENT_PROCESSOR, DataCategory.Error);
return SentryId.EMPTY_ID;
}

if (ErrorUtils.isIgnored(options.getIgnoredErrors(), event)) {
options
.getLogger()
.log(
SentryLevel.DEBUG,
"Event was dropped as the error %s is ignored",
event.getMessage());
lcian marked this conversation as resolved.
Show resolved Hide resolved
options
.getClientReportRecorder()
.recordLostEvent(DiscardReason.EVENT_PROCESSOR, DataCategory.Error);
return SentryId.EMPTY_ID;
}
}

if (shouldApplyScopeData(event, hint)) {
Expand Down
34 changes: 17 additions & 17 deletions sentry/src/main/java/io/sentry/SentryOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,10 @@ public class SentryOptions {
new CopyOnWriteArraySet<>();

/**
* Exception names or regex patterns that the captured exception will be tested against. If there
* is a match, the captured exception will not be sent to Sentry as {@link SentryEvent}.
* Strings or regex patterns that possible error messages for an event will be tested against. If
* there is a match, the captured event will not be sent to Sentry.
*/
private @Nullable List<FilterString> ignoredExceptions = null;
private @Nullable List<FilterString> ignoredErrors = null;

/**
* Code that provides middlewares, bindings or hooks into certain frameworks or environments,
Expand Down Expand Up @@ -1578,30 +1578,30 @@ boolean containsIgnoredExceptionForType(final @NotNull Throwable throwable) {
return this.ignoredExceptionsForType.contains(throwable.getClass());
}

public @Nullable List<FilterString> getIgnoredExceptions() {
return ignoredExceptions;
public @Nullable List<FilterString> getIgnoredErrors() {
return ignoredErrors;
}

public void setIgnoredExceptions(final @Nullable List<String> ignoredExceptions) {
if (ignoredExceptions == null) {
this.ignoredExceptions = null;
public void setIgnoredErrors(final @Nullable List<String> ignoredErrors) {
lcian marked this conversation as resolved.
Show resolved Hide resolved
if (ignoredErrors == null) {
this.ignoredErrors = null;
} else {
@NotNull final List<FilterString> patterns = new ArrayList<>();
for (String pattern : ignoredExceptions) {
for (String pattern : ignoredErrors) {
if (pattern != null && !pattern.isEmpty()) {
patterns.add(new FilterString(pattern));
}
}

this.ignoredExceptions = patterns;
this.ignoredErrors = patterns;
}
}

public void addIgnoredException(final @NotNull String pattern) {
if (ignoredExceptions == null) {
ignoredExceptions = new ArrayList<>();
public void addIgnoredError(final @NotNull String pattern) {
if (ignoredErrors == null) {
ignoredErrors = new ArrayList<>();
}
ignoredExceptions.add(new FilterString(pattern));
ignoredErrors.add(new FilterString(pattern));
}

/**
Expand Down Expand Up @@ -2833,9 +2833,9 @@ public void merge(final @NotNull ExternalOptions options) {
final List<String> ignoredTransactions = new ArrayList<>(options.getIgnoredTransactions());
setIgnoredTransactions(ignoredTransactions);
}
if (options.getIgnoredExceptions() != null) {
final List<String> ignoredExceptions = new ArrayList<>(options.getIgnoredExceptions());
setIgnoredExceptions(ignoredExceptions);
if (options.getIgnoredErrors() != null) {
final List<String> ignoredExceptions = new ArrayList<>(options.getIgnoredErrors());
setIgnoredErrors(ignoredExceptions);
}
if (options.isEnableBackpressureHandling() != null) {
setEnableBackpressureHandling(options.isEnableBackpressureHandling());
Expand Down
69 changes: 69 additions & 0 deletions sentry/src/main/java/io/sentry/util/ErrorUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package io.sentry.util;

import io.sentry.FilterString;
import io.sentry.SentryEvent;
import io.sentry.protocol.Message;
import io.sentry.protocol.SentryException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

public final class ErrorUtils {

/** Checks if an error has been ignored. */
@ApiStatus.Internal
public static boolean isIgnored(
final @Nullable List<FilterString> ignoredErrors, final @NotNull SentryEvent event) {
if (event == null || ignoredErrors == null || ignoredErrors.isEmpty()) {
return false;
}

final @NotNull Set<String> possibleMessages = new HashSet<>();
lcian marked this conversation as resolved.
Show resolved Hide resolved

final @Nullable Message eventMessage = event.getMessage();
if (eventMessage != null) {
final @Nullable String stringMessage = eventMessage.getMessage();
if (stringMessage != null) {
possibleMessages.add(stringMessage);
}
final @Nullable String formattedMessage = eventMessage.getFormatted();
if (formattedMessage != null) {
possibleMessages.add(formattedMessage);
}
}
final @Nullable List<SentryException> exceptions = event.getExceptions();
lcian marked this conversation as resolved.
Show resolved Hide resolved
if (exceptions != null && !exceptions.isEmpty()) {
for (final @Nullable SentryException exception : exceptions) {
if (exception != null) {
final @Nullable String value = exception.getValue();
if (value != null) {
possibleMessages.add(value);
}
}
}
}
final @Nullable Throwable throwable = event.getThrowable();
if (throwable != null) {
possibleMessages.add(throwable.toString());
}

for (final @NotNull FilterString filter : ignoredErrors) {
lcian marked this conversation as resolved.
Show resolved Hide resolved
if (possibleMessages.contains(filter.getFilterString())) {
return true;
}
}

for (final @NotNull FilterString filter : ignoredErrors) {
for (final @NotNull String message : possibleMessages) {
if (filter.matches(message)) {
return true;
}
}
}

return false;
}
}
37 changes: 2 additions & 35 deletions sentry/src/main/java/io/sentry/util/ExceptionUtils.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
package io.sentry.util;

import io.sentry.FilterString;
import java.util.List;
import java.util.Set;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

@ApiStatus.Internal
public final class ExceptionUtils {
Expand All @@ -27,39 +24,9 @@ public final class ExceptionUtils {

/** Checks if an exception has been ignored. */
@ApiStatus.Internal
public static @NotNull boolean isIgnored(
public static boolean isIgnored(
final @NotNull Set<Class<? extends Throwable>> ignoredExceptionsForType,
final @Nullable List<FilterString> ignoredExceptions,
final @NotNull Throwable throwable) {
if (throwable == null) {
return false;
}

final Class<? extends Throwable> throwableClass = throwable.getClass();
if (ignoredExceptionsForType.contains(throwableClass)) {
return true;
}

if (ignoredExceptions == null || ignoredExceptions.isEmpty()) {
return false;
}
final String throwableClassName = throwableClass.getCanonicalName();
if (throwableClassName == null) {
return false;
}

for (final FilterString filter : ignoredExceptions) {
if (filter.getFilterString().equals(throwableClassName)) {
return true;
}
}

for (final FilterString filter : ignoredExceptions) {
if (filter.matches(throwableClassName)) {
return true;
}
}

return false;
return ignoredExceptionsForType.contains(throwable.getClass());
}
}
Loading