Skip to content

Commit

Permalink
Add before_emit_metric callback to options (#3250)
Browse files Browse the repository at this point in the history
* Create a span when metrics.timing is called

* Add before_emit_metric callback to options

* Populate span tags with metric tags

* Update Changelog

* Address PR feedback
  • Loading branch information
markushi committed Mar 7, 2024
1 parent f4f39c7 commit 8282f8a
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

- Experimental: Add span summaries for metrics ([#3238](https://github.com/getsentry/sentry-java/pull/3238))
- Experimental: Implement span creating mode in metrics.timing API ([#3248](https://github.com/getsentry/sentry-java/pull/3248))
- Experimental: Add before_emit_metric callback to options ([#3250](https://github.com/getsentry/sentry-java/pull/3250))

## 7.5.0

Expand Down
8 changes: 7 additions & 1 deletion sentry/api/sentry.api
Original file line number Diff line number Diff line change
Expand Up @@ -1029,7 +1029,7 @@ public final class io/sentry/MemoryCollectionData {

public final class io/sentry/MetricsAggregator : io/sentry/IMetricsAggregator, java/io/Closeable, java/lang/Runnable {
public fun <init> (Lio/sentry/SentryOptions;Lio/sentry/metrics/IMetricsClient;)V
public fun <init> (Lio/sentry/metrics/IMetricsClient;Lio/sentry/ILogger;Lio/sentry/SentryDateProvider;ILio/sentry/ISentryExecutorService;)V
public fun <init> (Lio/sentry/metrics/IMetricsClient;Lio/sentry/ILogger;Lio/sentry/SentryDateProvider;ILio/sentry/SentryOptions$BeforeEmitMetricCallback;Lio/sentry/ISentryExecutorService;)V
public fun close ()V
public fun distribution (Ljava/lang/String;DLio/sentry/MeasurementUnit;Ljava/util/Map;JILio/sentry/metrics/LocalMetricsAggregator;)V
public fun flush (Z)V
Expand Down Expand Up @@ -2203,6 +2203,7 @@ public class io/sentry/SentryOptions {
public static fun empty ()Lio/sentry/SentryOptions;
public fun getBackpressureMonitor ()Lio/sentry/backpressure/IBackpressureMonitor;
public fun getBeforeBreadcrumb ()Lio/sentry/SentryOptions$BeforeBreadcrumbCallback;
public fun getBeforeEmitMetricCallback ()Lio/sentry/SentryOptions$BeforeEmitMetricCallback;
public fun getBeforeEnvelopeCallback ()Lio/sentry/SentryOptions$BeforeEnvelopeCallback;
public fun getBeforeSend ()Lio/sentry/SentryOptions$BeforeSendCallback;
public fun getBeforeSendTransaction ()Lio/sentry/SentryOptions$BeforeSendTransactionCallback;
Expand Down Expand Up @@ -2314,6 +2315,7 @@ public class io/sentry/SentryOptions {
public fun setAttachThreads (Z)V
public fun setBackpressureMonitor (Lio/sentry/backpressure/IBackpressureMonitor;)V
public fun setBeforeBreadcrumb (Lio/sentry/SentryOptions$BeforeBreadcrumbCallback;)V
public fun setBeforeEmitMetricCallback (Lio/sentry/SentryOptions$BeforeEmitMetricCallback;)V
public fun setBeforeEnvelopeCallback (Lio/sentry/SentryOptions$BeforeEnvelopeCallback;)V
public fun setBeforeSend (Lio/sentry/SentryOptions$BeforeSendCallback;)V
public fun setBeforeSendTransaction (Lio/sentry/SentryOptions$BeforeSendTransactionCallback;)V
Expand Down Expand Up @@ -2407,6 +2409,10 @@ public abstract interface class io/sentry/SentryOptions$BeforeBreadcrumbCallback
public abstract fun execute (Lio/sentry/Breadcrumb;Lio/sentry/Hint;)Lio/sentry/Breadcrumb;
}

public abstract interface class io/sentry/SentryOptions$BeforeEmitMetricCallback {
public abstract fun execute (Ljava/lang/String;Ljava/util/Map;)Z
}

public abstract interface class io/sentry/SentryOptions$BeforeEnvelopeCallback {
public abstract fun execute (Lio/sentry/SentryEnvelope;Lio/sentry/Hint;)V
}
Expand Down
10 changes: 10 additions & 0 deletions sentry/src/main/java/io/sentry/MetricsAggregator.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public final class MetricsAggregator implements IMetricsAggregator, Runnable, Cl
private final @NotNull ILogger logger;
private final @NotNull IMetricsClient client;
private final @NotNull SentryDateProvider dateProvider;
private final @Nullable SentryOptions.BeforeEmitMetricCallback beforeEmitCallback;

private volatile @NotNull ISentryExecutorService executorService;
private volatile boolean isClosed = false;
Expand All @@ -58,6 +59,7 @@ public MetricsAggregator(
options.getLogger(),
options.getDateProvider(),
MetricsHelper.MAX_TOTAL_WEIGHT,
options.getBeforeEmitMetricCallback(),
NoOpSentryExecutorService.getInstance());
}

Expand All @@ -67,11 +69,13 @@ public MetricsAggregator(
final @NotNull ILogger logger,
final @NotNull SentryDateProvider dateProvider,
final int maxWeight,
final @Nullable SentryOptions.BeforeEmitMetricCallback beforeEmitCallback,
final @NotNull ISentryExecutorService executorService) {
this.client = client;
this.logger = logger;
this.dateProvider = dateProvider;
this.maxWeight = maxWeight;
this.beforeEmitCallback = beforeEmitCallback;
this.executorService = executorService;
}

Expand Down Expand Up @@ -200,6 +204,12 @@ private void add(
return;
}

if (beforeEmitCallback != null) {
if (!beforeEmitCallback.execute(key, tags)) {
return;
}
}

final long timeBucketKey = MetricsHelper.getTimeBucketKey(timestampMs);
final @NotNull Map<String, Metric> timeBucket = getOrAddTimeBucket(timeBucketKey);

Expand Down
28 changes: 28 additions & 0 deletions sentry/src/main/java/io/sentry/SentryOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,8 @@ public class SentryOptions {

private boolean enableSpanLocalMetricAggregation = true;

private @Nullable BeforeEmitMetricCallback beforeEmitMetricCallback = null;

/**
* Profiling traces rate. 101 hz means 101 traces in 1 second. Defaults to 101 to avoid possible
* lockstep sampling. More on
Expand Down Expand Up @@ -2362,6 +2364,18 @@ public void setEnableDefaultTagsForMetrics(final boolean enableDefaultTagsForMet
this.enableDefaultTagsForMetrics = enableDefaultTagsForMetrics;
}

@ApiStatus.Experimental
@Nullable
public BeforeEmitMetricCallback getBeforeEmitMetricCallback() {
return beforeEmitMetricCallback;
}

@ApiStatus.Experimental
public void setBeforeEmitMetricCallback(
final @Nullable BeforeEmitMetricCallback beforeEmitMetricCallback) {
this.beforeEmitMetricCallback = beforeEmitMetricCallback;
}

public @Nullable Cron getCron() {
return cron;
}
Expand Down Expand Up @@ -2455,6 +2469,20 @@ public interface BeforeEnvelopeCallback {
void execute(@NotNull SentryEnvelope envelope, @Nullable Hint hint);
}

/** The BeforeEmitMetric callback */
@ApiStatus.Experimental
public interface BeforeEmitMetricCallback {

/**
* A callback which gets called right before a metric is about to be emitted.
*
* @param key the metric key
* @param tags the metric tags
* @return true if the metric should be emitted, false otherwise
*/
boolean execute(@NotNull String key, @Nullable Map<String, String> tags);
}

/**
* Creates SentryOptions instance without initializing any of the internal parts.
*
Expand Down
42 changes: 41 additions & 1 deletion sentry/src/test/java/io/sentry/MetricsAggregatorTest.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.sentry

import io.sentry.SentryOptions.BeforeEmitMetricCallback
import io.sentry.metrics.IMetricsClient
import io.sentry.metrics.LocalMetricsAggregator
import io.sentry.metrics.MetricType
Expand Down Expand Up @@ -30,12 +31,16 @@ class MetricsAggregatorTest {
var currentTimeMillis: Long = 0
var executorService = DeferredExecutorService()

fun getSut(maxWeight: Int = MetricsHelper.MAX_TOTAL_WEIGHT): MetricsAggregator {
fun getSut(
maxWeight: Int = MetricsHelper.MAX_TOTAL_WEIGHT,
beforeEmitMetricCallback: BeforeEmitMetricCallback? = null
): MetricsAggregator {
return MetricsAggregator(
client,
logger,
dateProvider,
maxWeight,
beforeEmitMetricCallback,
executorService
)
}
Expand Down Expand Up @@ -497,4 +502,39 @@ class MetricsAggregatorTest {
// flush is scheduled for later
verify(fixture.executorService).schedule(any(), eq(MetricsHelper.FLUSHER_SLEEP_TIME_MS))
}

@Test
fun `key and tags are passed down to beforeEmitMetricCallback`() {
var lastKey: String? = null
var lastTags: Map<String, String>? = null
val aggregator = fixture.getSut(beforeEmitMetricCallback = { key, tags ->
lastKey = key
lastTags = tags
return@getSut false
})

val key = "metric-key"
val tags = mapOf(
"tag-key" to "tag-value"
)
aggregator.increment(key, 1.0, null, tags, 20_001, 1, null)
assertEquals(key, lastKey)
assertEquals(tags, lastTags)
}

@Test
fun `if before emit callback returns true, metric is emitted`() {
val aggregator = fixture.getSut(beforeEmitMetricCallback = { key, tags -> true })
aggregator.increment("key", 1.0, null, null, 20_001, 1, null)
aggregator.flush(true)
verify(fixture.client).captureMetrics(any())
}

@Test
fun `if before emit callback returns false, metric is not emitted`() {
val aggregator = fixture.getSut(beforeEmitMetricCallback = { key, tags -> false })
aggregator.increment("key", 1.0, null, null, 20_001, 1, null)
aggregator.flush(true)
verify(fixture.client, never()).captureMetrics(any())
}
}
15 changes: 15 additions & 0 deletions sentry/src/test/java/io/sentry/SentryOptionsTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import kotlin.test.assertIs
import kotlin.test.assertNotEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull
import kotlin.test.assertSame
import kotlin.test.assertTrue

class SentryOptionsTest {
Expand Down Expand Up @@ -651,6 +652,20 @@ class SentryOptionsTest {
assertFalse(options.isEnableSpanLocalMetricAggregation)
}

@Test
fun `metric callback is null by default`() {
assertNull(SentryOptions().beforeEmitMetricCallback)
}

@Test
fun `when metric callback is set, getter returns it`() {
val callback = SentryOptions.BeforeEmitMetricCallback { _, _ -> false }
val options = SentryOptions().apply {
beforeEmitMetricCallback = callback
}
assertSame(callback, options.beforeEmitMetricCallback)
}

@Test
fun `existing cron defaults are not overridden if not present in external options`() {
val options = SentryOptions().apply {
Expand Down

0 comments on commit 8282f8a

Please sign in to comment.