Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
```
- Fix abstract method error in `SentrySupportSQLiteDatabase` ([#4597](https://github.com/getsentry/sentry-java/pull/4597))

### Features

- Add onDiscard to enable users to track the type and amount of data discarded before reaching Sentry ([#4612](https://github.com/getsentry/sentry-java/pull/4612))
Comment thread
lcian marked this conversation as resolved.
Outdated

## 8.18.0

### Features
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
package io.sentry.samples.console;

import io.sentry.Breadcrumb;
import io.sentry.EventProcessor;
import io.sentry.Hint;
import io.sentry.ISpan;
import io.sentry.ITransaction;
import io.sentry.Sentry;
import io.sentry.SentryEvent;
import io.sentry.SentryLevel;
import io.sentry.SpanStatus;
import io.sentry.*;
import io.sentry.clientreport.DiscardReason;
import io.sentry.protocol.Message;
import io.sentry.protocol.User;
import java.util.Collections;

public class Main {

private static long numberOfDiscardedSpansDueToOverflow = 0;

public static void main(String[] args) throws InterruptedException {
Sentry.init(
options -> {
Expand Down Expand Up @@ -59,6 +54,18 @@ public static void main(String[] args) throws InterruptedException {
return breadcrumb;
});

// Record data being discarded, including the reason, type of data, and the number of
// items dropped
options.setOnDiscard(
(reason, category, number) -> {
// Only record the number of lost spans due to overflow conditions
if ((reason.equals(DiscardReason.CACHE_OVERFLOW.getReason())
|| reason.equals(DiscardReason.QUEUE_OVERFLOW.getReason()))
&& category.equals(DataCategory.Span.getCategory())) {
numberOfDiscardedSpansDueToOverflow += number;
}
});

// Configure the background worker which sends events to sentry:
// Wait up to 5 seconds before shutdown while there are events to send.
options.setShutdownTimeoutMillis(5000);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package io.sentry.samples.servlet;

import io.sentry.DataCategory;
import io.sentry.Sentry;
import io.sentry.clientreport.DiscardReason;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
Expand All @@ -9,6 +11,8 @@
@WebListener
public final class SentryInitializer implements ServletContextListener {

private static long numberOfDiscardedSpansDueToOverflow = 0;

@Override
public void contextInitialized(ServletContextEvent sce) {
Sentry.init(
Expand Down Expand Up @@ -57,6 +61,18 @@ public void contextInitialized(ServletContextEvent sce) {
return breadcrumb;
});

// Record data being discarded, including the reason, type of data, and the number of
// items dropped
options.setOnDiscard(
(reason, category, number) -> {
// Only record the number of lost spans due to overflow conditions
if ((reason.equals(DiscardReason.CACHE_OVERFLOW.getReason())
|| reason.equals(DiscardReason.QUEUE_OVERFLOW.getReason()))
&& category.equals(DataCategory.Span.getCategory())) {
numberOfDiscardedSpansDueToOverflow += number;
Comment thread
alexander-alderman-webb marked this conversation as resolved.
}
});
Comment thread
alexander-alderman-webb marked this conversation as resolved.

// Configure the background worker which sends events to sentry:
// Wait up to 5 seconds before shutdown while there are events to send.
options.setShutdownTimeoutMillis(5000);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ static class HubConfiguration {
beforeSendLogsCallback,
final @NotNull ObjectProvider<SentryOptions.BeforeBreadcrumbCallback>
beforeBreadcrumbCallback,
final @NotNull ObjectProvider<SentryOptions.OnDiscardCallback> onDiscardCallback,
final @NotNull ObjectProvider<SentryOptions.TracesSamplerCallback> tracesSamplerCallback,
final @NotNull List<EventProcessor> eventProcessors,
final @NotNull List<Integration> integrations,
Expand All @@ -118,6 +119,7 @@ static class HubConfiguration {
beforeSendTransactionCallback.ifAvailable(options::setBeforeSendTransaction);
beforeSendLogsCallback.ifAvailable(callback -> options.getLogs().setBeforeSend(callback));
beforeBreadcrumbCallback.ifAvailable(options::setBeforeBreadcrumb);
onDiscardCallback.ifAvailable(options::setOnDiscard);
tracesSamplerCallback.ifAvailable(options::setTracesSampler);
eventProcessors.forEach(options::addEventProcessor);
integrations.forEach(options::addIntegration);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,17 @@ class SentryAutoConfigurationTest {
}
}

@Test
fun `registers onDiscardCallback on SentryOptions`() {
contextRunner
.withPropertyValues("sentry.dsn=http://key@localhost/proj")
.withUserConfiguration(CustomOnDiscardCallbackConfiguration::class.java)
.run {
assertThat(it.getBean(SentryOptions::class.java).onDiscard)
.isInstanceOf(CustomOnDiscardCallback::class.java)
}
}

@Test
fun `registers event processor on SentryOptions`() {
contextRunner
Expand Down Expand Up @@ -1137,6 +1148,16 @@ class SentryAutoConfigurationTest {
override fun execute(breadcrumb: Breadcrumb, hint: Hint): Breadcrumb? = null
}

@Configuration(proxyBeanMethods = false)
open class CustomOnDiscardCallbackConfiguration {

@Bean open fun onDiscardCallback() = CustomOnDiscardCallback()
}

class CustomOnDiscardCallback : SentryOptions.OnDiscardCallback {
override fun execute(reason: String, category: String, countToAdd: Long) {}
}

@Configuration(proxyBeanMethods = false)
open class CustomEventProcessorConfiguration {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ static class HubConfiguration {
beforeSendLogsCallback,
final @NotNull ObjectProvider<SentryOptions.BeforeBreadcrumbCallback>
beforeBreadcrumbCallback,
final @NotNull ObjectProvider<SentryOptions.OnDiscardCallback> onDiscardCallback,
final @NotNull ObjectProvider<SentryOptions.TracesSamplerCallback> tracesSamplerCallback,
final @NotNull List<EventProcessor> eventProcessors,
final @NotNull List<Integration> integrations,
Expand All @@ -116,6 +117,7 @@ static class HubConfiguration {
beforeSendTransactionCallback.ifAvailable(options::setBeforeSendTransaction);
beforeSendLogsCallback.ifAvailable(callback -> options.getLogs().setBeforeSend(callback));
beforeBreadcrumbCallback.ifAvailable(options::setBeforeBreadcrumb);
onDiscardCallback.ifAvailable(options::setOnDiscard);
tracesSamplerCallback.ifAvailable(options::setTracesSampler);
eventProcessors.forEach(options::addEventProcessor);
integrations.forEach(options::addIntegration);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,17 @@ class SentryAutoConfigurationTest {
}
}

@Test
fun `registers onDiscardCallback on SentryOptions`() {
contextRunner
.withPropertyValues("sentry.dsn=http://key@localhost/proj")
.withUserConfiguration(CustomOnDiscardCallbackConfiguration::class.java)
.run {
assertThat(it.getBean(SentryOptions::class.java).onDiscard)
.isInstanceOf(CustomOnDiscardCallback::class.java)
}
}

@Test
fun `registers event processor on SentryOptions`() {
contextRunner
Expand Down Expand Up @@ -1063,6 +1074,16 @@ class SentryAutoConfigurationTest {
override fun execute(breadcrumb: Breadcrumb, hint: Hint): Breadcrumb? = null
}

@Configuration(proxyBeanMethods = false)
open class CustomOnDiscardCallbackConfiguration {

@Bean open fun onDiscardCallback() = CustomOnDiscardCallback()
}

class CustomOnDiscardCallback : SentryOptions.OnDiscardCallback {
override fun execute(reason: String, category: String, countToAdd: Long) {}
}

@Configuration(proxyBeanMethods = false)
open class CustomEventProcessorConfiguration {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ public SentryInitBeanPostProcessor() {
applicationContext
.getBeanProvider(SentryOptions.BeforeBreadcrumbCallback.class)
.ifAvailable(options::setBeforeBreadcrumb);
applicationContext
.getBeanProvider(SentryOptions.OnDiscardCallback.class)
.ifAvailable(options::setOnDiscard);
applicationContext
.getBeansOfType(EventProcessor.class)
.values()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,11 @@ class EnableSentryTest {
@Bean fun beforeBreadcrumbCallback() = mock<SentryOptions.BeforeBreadcrumbCallback>()
}

@EnableSentry(dsn = "http://key@localhost/proj")
class AppConfigWithCustomOnDiscardCallback {
@Bean fun onDiscardCallback() = mock<SentryOptions.OnDiscardCallback>()
}

@EnableSentry(dsn = "http://key@localhost/proj")
class AppConfigWithCustomEventProcessors {
@Bean fun firstProcessor() = mock<EventProcessor>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ public SentryInitBeanPostProcessor() {
applicationContext
.getBeanProvider(SentryOptions.BeforeBreadcrumbCallback.class)
.ifAvailable(options::setBeforeBreadcrumb);
applicationContext
.getBeanProvider(SentryOptions.OnDiscardCallback.class)
.ifAvailable(options::setOnDiscard);
applicationContext
.getBeansOfType(EventProcessor.class)
.values()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,11 @@ class EnableSentryTest {
@Bean fun beforeBreadcrumbCallback() = mock<SentryOptions.BeforeBreadcrumbCallback>()
}

@EnableSentry(dsn = "http://key@localhost/proj")
class AppConfigWithCustomOnDiscardCallback {
@Bean fun onDiscardCallback() = mock<SentryOptions.OnDiscardCallback>()
}

@EnableSentry(dsn = "http://key@localhost/proj")
class AppConfigWithCustomEventProcessors {
@Bean fun firstProcessor() = mock<EventProcessor>()
Expand Down
6 changes: 6 additions & 0 deletions sentry/api/sentry.api
Original file line number Diff line number Diff line change
Expand Up @@ -3337,6 +3337,7 @@ public class io/sentry/SentryOptions {
public fun getMaxSpans ()I
public fun getMaxTraceFileSize ()J
public fun getModulesLoader ()Lio/sentry/internal/modules/IModulesLoader;
public fun getOnDiscard ()Lio/sentry/SentryOptions$OnDiscardCallback;
public fun getOpenTelemetryMode ()Lio/sentry/SentryOpenTelemetryMode;
public fun getOptionsObservers ()Ljava/util/List;
public fun getOutboxPath ()Ljava/lang/String;
Expand Down Expand Up @@ -3479,6 +3480,7 @@ public class io/sentry/SentryOptions {
public fun setMaxSpans (I)V
public fun setMaxTraceFileSize (J)V
public fun setModulesLoader (Lio/sentry/internal/modules/IModulesLoader;)V
public fun setOnDiscard (Lio/sentry/SentryOptions$OnDiscardCallback;)V
public fun setOpenTelemetryMode (Lio/sentry/SentryOpenTelemetryMode;)V
public fun setPrintUncaughtStackTrace (Z)V
public fun setProfileLifecycle (Lio/sentry/ProfileLifecycle;)V
Expand Down Expand Up @@ -3572,6 +3574,10 @@ public abstract interface class io/sentry/SentryOptions$Logs$BeforeSendLogCallba
public abstract fun execute (Lio/sentry/SentryLogEvent;)Lio/sentry/SentryLogEvent;
}

public abstract interface class io/sentry/SentryOptions$OnDiscardCallback {
public abstract fun execute (Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;)V
}

public abstract interface class io/sentry/SentryOptions$ProfilesSamplerCallback {
public abstract fun sample (Lio/sentry/SamplingContext;)Ljava/lang/Double;
}
Expand Down
34 changes: 34 additions & 0 deletions sentry/src/main/java/io/sentry/SentryOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,9 @@ public class SentryOptions {
*/
private @Nullable BeforeBreadcrumbCallback beforeBreadcrumb;

/** Invoked when some data from the SDK is dropped before being consumed by Sentry */
private @Nullable OnDiscardCallback onDiscard;

/** The cache dir. path for caching offline events */
private @Nullable String cacheDirPath;

Expand Down Expand Up @@ -904,6 +907,24 @@ public void setBeforeBreadcrumb(@Nullable BeforeBreadcrumbCallback beforeBreadcr
this.beforeBreadcrumb = beforeBreadcrumb;
}

/**
* Returns the onDiscard callback
*
* @return the onDiscard callback or null if not set
*/
public @Nullable OnDiscardCallback getOnDiscard() {
return onDiscard;
}

/**
* Sets the onDiscard callback
*
* @param onDiscard the onDiscard callback
*/
public void setOnDiscard(@Nullable OnDiscardCallback onDiscard) {
this.onDiscard = onDiscard;
}

/**
* Returns the cache dir. path if set
*
Expand Down Expand Up @@ -2982,6 +3003,19 @@ public interface BeforeBreadcrumbCallback {
Breadcrumb execute(@NotNull Breadcrumb breadcrumb, @NotNull Hint hint);
}

/** The OnDiscard callback */
public interface OnDiscardCallback {

/**
* Best-effort record of data discarded before reaching Sentry
*
* @param reason the reason data was dropped, corresponding to a DiscardReason
* @param category the type of data discarded, corresponding to a DataCategory
* @param number the number of discarded data items
*/
void execute(@NotNull String reason, @NotNull String category, @NotNull Long number);
Comment thread
alexander-alderman-webb marked this conversation as resolved.
Outdated
}

/** The traces sampler callback. */
public interface TracesSamplerCallback {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,18 @@ private void recordLostEventInternal(
@NotNull String reason, @NotNull String category, @NotNull Long countToAdd) {
Comment thread
alexander-alderman-webb marked this conversation as resolved.
Outdated
final ClientReportKey key = new ClientReportKey(reason, category);
storage.addCount(key, countToAdd);
if (options.getOnDiscard() != null) {
try {
options.getOnDiscard().execute(reason, category, countToAdd);
Copy link
Copy Markdown
Contributor Author

@alexander-alderman-webb alexander-alderman-webb Aug 7, 2025

Choose a reason for hiding this comment

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

There may be duplicate invocations of onDiscard() after restoreCountsFromClientReport() is reached. We're discussing whether to change the signature on the callback, so I will address this when we have a decision.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

See my comment above

} catch (Throwable e) {
options
.getLogger()
.log(
SentryLevel.ERROR,
"The onDiscard callback threw an exception. It will be added as breadcrumb and continue.",
e);
Comment thread
cursor[bot] marked this conversation as resolved.
Outdated
}
}
}

@Nullable
Expand Down
Loading
Loading