Skip to content
Merged
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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@
- In this mode the SDK makes use of `GlobalOpenTelemetry`
- Automatically set span factory based on presence of OpenTelemetry ([#3858](https://github.com/getsentry/sentry-java/pull/3858))
- `SentrySpanFactoryHolder` has been removed as it is no longer required.
- Add `ignoredTransactions` option to filter out transactions by name ([#3871](https://github.com/getsentry/sentry-java/pull/3871))
- can be used via ENV vars, e.g. `SENTRY_IGNORED_TRANSACTIONS=POST /person/,GET /pers.*`
- can also be set in options directly, e.g. `options.setIgnoredTransactions(...)`
- can also be set in `sentry.properties`, e.g. `ignored-transactions=POST /person/,GET /pers.*`
- can also be set in Spring config `application.properties`, e.g. `sentry.ignored-transactions=POST /person/,GET /pers.*`
- Add a sample for showcasing Sentry with OpenTelemetry for Spring Boot 3 with our Java agent (`sentry-samples-spring-boot-jakarta-opentelemetry`) ([#3856](https://github.com/getsentry/sentry-java/pull/3828))
- Add a sample for showcasing Sentry with OpenTelemetry for Spring Boot 3 without our Java agent (`sentry-samples-spring-boot-jakarta-opentelemetry-noagent`) ([#3856](https://github.com/getsentry/sentry-java/pull/3856))
- Add a sample for showcasing Sentry with OpenTelemetry (`sentry-samples-console-opentelemetry-noagent`) ([#3856](https://github.com/getsentry/sentry-java/pull/3862))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ class SentryAutoConfigurationTest {
"sentry.enabled=false",
"sentry.send-modules=false",
"sentry.ignored-checkins=slug1,slugB",
"sentry.ignored-transactions=transactionName1,transactionNameB",
"sentry.enable-backpressure-handling=false",
"sentry.enable-spotlight=true",
"sentry.spotlight-connection-url=http://local.sentry.io:1234",
Expand Down Expand Up @@ -213,6 +214,7 @@ class SentryAutoConfigurationTest {
assertThat(options.isEnabled).isEqualTo(false)
assertThat(options.isSendModules).isEqualTo(false)
assertThat(options.ignoredCheckIns).containsOnly("slug1", "slugB")
assertThat(options.ignoredTransactions).containsOnly("transactionName1", "transactionNameB")
assertThat(options.isEnableBackpressureHandling).isEqualTo(false)
assertThat(options.isForceInit).isEqualTo(true)
assertThat(options.isGlobalHubMode).isEqualTo(true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ class SentryAutoConfigurationTest {
"sentry.enabled=false",
"sentry.send-modules=false",
"sentry.ignored-checkins=slug1,slugB",
"sentry.ignored-transactions=transactionName1,transactionNameB",
"sentry.enable-backpressure-handling=false",
"sentry.enable-spotlight=true",
"sentry.spotlight-connection-url=http://local.sentry.io:1234",
Expand Down Expand Up @@ -209,6 +210,7 @@ class SentryAutoConfigurationTest {
assertThat(options.isEnabled).isEqualTo(false)
assertThat(options.isSendModules).isEqualTo(false)
assertThat(options.ignoredCheckIns).containsOnly("slug1", "slugB")
assertThat(options.ignoredTransactions).containsOnly("transactionName1", "transactionNameB")
assertThat(options.isEnableBackpressureHandling).isEqualTo(false)
assertThat(options.isForceInit).isEqualTo(true)
assertThat(options.isGlobalHubMode).isEqualTo(true)
Expand Down
5 changes: 5 additions & 0 deletions sentry/api/sentry.api
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,7 @@ public final class io/sentry/ExternalOptions {
public fun getIdleTimeout ()Ljava/lang/Long;
public fun getIgnoredCheckIns ()Ljava/util/List;
public fun getIgnoredExceptionsForType ()Ljava/util/Set;
public fun getIgnoredTransactions ()Ljava/util/List;
public fun getInAppExcludes ()Ljava/util/List;
public fun getInAppIncludes ()Ljava/util/List;
public fun getMaxRequestBodySize ()Lio/sentry/SentryOptions$RequestSize;
Expand Down Expand Up @@ -492,6 +493,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 setIgnoredTransactions (Ljava/util/List;)V
public fun setMaxRequestBodySize (Lio/sentry/SentryOptions$RequestSize;)V
public fun setPrintUncaughtStackTrace (Ljava/lang/Boolean;)V
public fun setProfilesSampleRate (Ljava/lang/Double;)V
Expand Down Expand Up @@ -2826,6 +2828,7 @@ public class io/sentry/SentryOptions {
public fun getIgnoredCheckIns ()Ljava/util/List;
public fun getIgnoredExceptionsForType ()Ljava/util/Set;
public fun getIgnoredSpanOrigins ()Ljava/util/List;
public fun getIgnoredTransactions ()Ljava/util/List;
public fun getInAppExcludes ()Ljava/util/List;
public fun getInAppIncludes ()Ljava/util/List;
public fun getInitPriority ()Lio/sentry/InitPriority;
Expand Down Expand Up @@ -2953,6 +2956,7 @@ public class io/sentry/SentryOptions {
public fun setIdleTimeout (Ljava/lang/Long;)V
public fun setIgnoredCheckIns (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
public fun setInstrumenter (Lio/sentry/Instrumenter;)V
public fun setLogger (Lio/sentry/ILogger;)V
Expand Down Expand Up @@ -6193,6 +6197,7 @@ public final class io/sentry/util/StringUtils {

public final class io/sentry/util/TracingUtils {
public fun <init> ()V
public static fun isIgnored (Ljava/util/List;Ljava/lang/String;)Z
public static fun maybeUpdateBaggage (Lio/sentry/IScope;Lio/sentry/SentryOptions;)Lio/sentry/PropagationContext;
public static fun startNewTrace (Lio/sentry/IScopes;)V
public static fun trace (Lio/sentry/IScopes;Ljava/util/List;Lio/sentry/ISpan;)Lio/sentry/util/TracingUtils$TracingHeaders;
Expand Down
10 changes: 10 additions & 0 deletions sentry/src/main/java/io/sentry/ExternalOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public final class ExternalOptions {
private @Nullable String spotlightConnectionUrl;

private @Nullable List<String> ignoredCheckIns;
private @Nullable List<String> ignoredTransactions;

private @Nullable Boolean sendModules;
private @Nullable Boolean sendDefaultPii;
Expand Down Expand Up @@ -138,6 +139,7 @@ public final class ExternalOptions {
options.setSendDefaultPii(propertiesProvider.getBooleanProperty("send-default-pii"));

options.setIgnoredCheckIns(propertiesProvider.getList("ignored-checkins"));
options.setIgnoredTransactions(propertiesProvider.getList("ignored-transactions"));

options.setEnableBackpressureHandling(
propertiesProvider.getBooleanProperty("enable-backpressure-handling"));
Expand Down Expand Up @@ -430,6 +432,14 @@ public void setIgnoredCheckIns(final @Nullable List<String> ignoredCheckIns) {
return ignoredCheckIns;
}

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

public @Nullable List<String> getIgnoredTransactions() {
return ignoredTransactions;
}

@ApiStatus.Experimental
public void setEnableBackpressureHandling(final @Nullable Boolean enableBackpressureHandling) {
this.enableBackpressureHandling = enableBackpressureHandling;
Expand Down
24 changes: 20 additions & 4 deletions sentry/src/main/java/io/sentry/SentryClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -774,6 +774,23 @@ public void captureSession(final @NotNull Session session, final @Nullable Hint
.getLogger()
.log(SentryLevel.DEBUG, "Capturing transaction: %s", transaction.getEventId());

if (TracingUtils.isIgnored(options.getIgnoredTransactions(), transaction.getTransaction())) {
options
.getLogger()
.log(
SentryLevel.DEBUG,
"Transaction was dropped as transaction name %s is ignored",
transaction.getTransaction());
options
.getClientReportRecorder()
.recordLostEvent(DiscardReason.EVENT_PROCESSOR, DataCategory.Transaction);
options
.getClientReportRecorder()
.recordLostEvent(
DiscardReason.EVENT_PROCESSOR, DataCategory.Span, transaction.getSpans().size() + 1);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

What does the +1 here do? Is that to account for the transaction?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes, same as further down.

return SentryId.EMPTY_ID;
}

SentryId sentryId = SentryId.EMPTY_ID;
if (transaction.getEventId() != null) {
sentryId = transaction.getEventId();
Expand Down Expand Up @@ -880,10 +897,9 @@ public void captureSession(final @NotNull Session session, final @Nullable Hint
SentryLevel.DEBUG,
"Check-in was dropped as slug %s is ignored",
checkIn.getMonitorSlug());
// TODO in a follow up PR with DataCategory.Monitor
// options
// .getClientReportRecorder()
// .recordLostEvent(DiscardReason.EVENT_PROCESSOR, DataCategory.Error);
options
.getClientReportRecorder()
.recordLostEvent(DiscardReason.EVENT_PROCESSOR, DataCategory.Monitor);
return SentryId.EMPTY_ID;
}

Expand Down
26 changes: 26 additions & 0 deletions sentry/src/main/java/io/sentry/SentryOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,8 @@ public class SentryOptions {
/** Contains a list of span origins for which spans / transactions should not be created. */
@ApiStatus.Experimental private @Nullable List<String> ignoredSpanOrigins = null;

private @Nullable List<String> ignoredTransactions = null;

@ApiStatus.Experimental
private @NotNull IBackpressureMonitor backpressureMonitor = NoOpBackpressureMonitor.getInstance();

Expand Down Expand Up @@ -2186,6 +2188,26 @@ public void setIgnoredSpanOrigins(final @Nullable List<String> ignoredSpanOrigin
return ignoredCheckIns;
}

public @Nullable List<String> getIgnoredTransactions() {
return ignoredTransactions;
}

@ApiStatus.Experimental
public void setIgnoredTransactions(final @Nullable List<String> ignoredTransactions) {
if (ignoredTransactions == null) {
this.ignoredTransactions = null;
} else {
@NotNull final List<String> filtered = new ArrayList<>();
for (String transactionName : ignoredTransactions) {
if (transactionName != null && !transactionName.isEmpty()) {
filtered.add(transactionName);
}
}

this.ignoredTransactions = filtered;
}
}

/** Returns the current {@link SentryDateProvider} that is used to retrieve the current date. */
@ApiStatus.Internal
public @NotNull SentryDateProvider getDateProvider() {
Expand Down Expand Up @@ -2667,6 +2689,10 @@ public void merge(final @NotNull ExternalOptions options) {
final List<String> ignoredCheckIns = new ArrayList<>(options.getIgnoredCheckIns());
setIgnoredCheckIns(ignoredCheckIns);
}
if (options.getIgnoredTransactions() != null) {
final List<String> ignoredTransactions = new ArrayList<>(options.getIgnoredTransactions());
setIgnoredTransactions(ignoredTransactions);
}
if (options.isEnableBackpressureHandling() != null) {
setEnableBackpressureHandling(options.isEnableBackpressureHandling());
}
Expand Down
29 changes: 29 additions & 0 deletions sentry/src/main/java/io/sentry/util/TracingUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import io.sentry.SentryOptions;
import io.sentry.SentryTraceHeader;
import java.util.List;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

Expand Down Expand Up @@ -116,4 +117,32 @@ public TracingHeaders(
return baggageHeader;
}
}

/** Checks if a transaction is to be ignored. */
@ApiStatus.Internal
public static boolean isIgnored(
final @Nullable List<String> ignoredTransactions, final @Nullable String transactionName) {
if (transactionName == null) {
return false;
}
if (ignoredTransactions == null || ignoredTransactions.isEmpty()) {
return false;
}

for (final String ignoredSlug : ignoredTransactions) {
if (ignoredSlug.equalsIgnoreCase(transactionName)) {
return true;
}

try {
if (transactionName.matches(ignoredSlug)) {
return true;
}
} catch (Throwable t) {
// ignore invalid regex
}
}

return false;
}
}
7 changes: 7 additions & 0 deletions sentry/src/test/java/io/sentry/ExternalOptionsTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,13 @@ class ExternalOptionsTest {
}
}

@Test
fun `creates options with ignoredTransactions`() {
withPropertiesFile("ignored-transactions=transactionName1,transactionName2") { options ->
assertTrue(options.ignoredTransactions!!.containsAll(listOf("transactionName1", "transactionName2")))
}
}

@Test
fun `creates options with enableBackpressureHandling set to false`() {
withPropertiesFile("enable-backpressure-handling=false") { options ->
Expand Down
44 changes: 44 additions & 0 deletions sentry/src/test/java/io/sentry/SentryClientTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -819,6 +819,50 @@ class SentryClientTest {
)
}

@Test
fun `transaction dropped by ignoredTransactions is recorded`() {
Comment thread
adinauer marked this conversation as resolved.
fixture.sentryOptions.ignoredTransactions = listOf("a-transaction")

val transaction = SentryTransaction(fixture.sentryTracer)

val eventId =
fixture.getSut().captureTransaction(transaction, fixture.sentryTracer.traceContext())

verify(fixture.transport, never()).send(any(), anyOrNull())

assertClientReport(
fixture.sentryOptions.clientReportRecorder,
listOf(
DiscardedEvent(DiscardReason.EVENT_PROCESSOR.reason, DataCategory.Transaction.category, 1),
DiscardedEvent(DiscardReason.EVENT_PROCESSOR.reason, DataCategory.Span.category, 2)
)
)

assertEquals(SentryId.EMPTY_ID, eventId)
}

@Test
fun `transaction dropped by ignoredTransactions with regex is recorded`() {
fixture.sentryOptions.ignoredTransactions = listOf("a.*action")

val transaction = SentryTransaction(fixture.sentryTracer)

val eventId =
fixture.getSut().captureTransaction(transaction, fixture.sentryTracer.traceContext())

verify(fixture.transport, never()).send(any(), anyOrNull())

assertClientReport(
fixture.sentryOptions.clientReportRecorder,
listOf(
DiscardedEvent(DiscardReason.EVENT_PROCESSOR.reason, DataCategory.Transaction.category, 1),
DiscardedEvent(DiscardReason.EVENT_PROCESSOR.reason, DataCategory.Span.category, 2)
)
)

assertEquals(SentryId.EMPTY_ID, eventId)
}

@Test
fun `backfillable events are only wired through backfilling processors`() {
val backfillingProcessor = mock<BackfillingEventProcessor>()
Expand Down
2 changes: 2 additions & 0 deletions sentry/src/test/java/io/sentry/SentryOptionsTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,7 @@ class SentryOptionsTest {
externalOptions.isEnablePrettySerializationOutput = false
externalOptions.isSendModules = false
externalOptions.ignoredCheckIns = listOf("slug1", "slug-B")
externalOptions.ignoredTransactions = listOf("transactionName1", "transaction-name-B")
externalOptions.isEnableBackpressureHandling = false
externalOptions.maxRequestBodySize = SentryOptions.RequestSize.MEDIUM
externalOptions.isSendDefaultPii = true
Expand Down Expand Up @@ -368,6 +369,7 @@ class SentryOptionsTest {
assertFalse(options.isEnablePrettySerializationOutput)
assertFalse(options.isSendModules)
assertEquals(listOf("slug1", "slug-B"), options.ignoredCheckIns)
assertEquals(listOf("transactionName1", "transaction-name-B"), options.ignoredTransactions)
assertFalse(options.isEnableBackpressureHandling)
assertTrue(options.isForceInit)
assertNotNull(options.cron)
Expand Down