From 5a44c4880a85ba3e5b7091bfb8bf489717c92e90 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 27 Sep 2022 16:18:03 +0200 Subject: [PATCH 1/6] Add custom measurements --- .../android/core/ActivityFramesTracker.java | 12 +++--- .../PerformanceAndroidEventProcessor.java | 5 ++- .../PerformanceAndroidEventProcessorTest.kt | 4 +- sentry/api/sentry.api | 27 +++++++++++++- sentry/src/main/java/io/sentry/ISpan.java | 23 ++++++++++++ sentry/src/main/java/io/sentry/NoOpSpan.java | 7 ++++ .../main/java/io/sentry/NoOpTransaction.java | 7 ++++ .../java/io/sentry/SentryMeasurementUnit.java | 37 +++++++++++++++++++ .../src/main/java/io/sentry/SentryTracer.java | 18 +++++++++ sentry/src/main/java/io/sentry/Span.java | 11 ++++++ .../io/sentry/protocol/MeasurementValue.java | 3 -- 11 files changed, 140 insertions(+), 14 deletions(-) create mode 100644 sentry/src/main/java/io/sentry/SentryMeasurementUnit.java diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/ActivityFramesTracker.java b/sentry-android-core/src/main/java/io/sentry/android/core/ActivityFramesTracker.java index d71cab5213c..46972447784 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/ActivityFramesTracker.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/ActivityFramesTracker.java @@ -1,11 +1,10 @@ package io.sentry.android.core; -import static io.sentry.protocol.MeasurementValue.NONE_UNIT; - import android.app.Activity; import android.util.SparseIntArray; import androidx.core.app.FrameMetricsAggregator; import io.sentry.ILogger; +import io.sentry.SentryMeasurementUnit; import io.sentry.protocol.MeasurementValue; import io.sentry.protocol.SentryId; import java.util.HashMap; @@ -104,9 +103,12 @@ public synchronized void setMetrics( return; } - final MeasurementValue tfValues = new MeasurementValue(totalFrames, NONE_UNIT); - final MeasurementValue sfValues = new MeasurementValue(slowFrames, NONE_UNIT); - final MeasurementValue ffValues = new MeasurementValue(frozenFrames, NONE_UNIT); + final MeasurementValue tfValues = + new MeasurementValue(totalFrames, SentryMeasurementUnit.NONE.apiName()); + final MeasurementValue sfValues = + new MeasurementValue(slowFrames, SentryMeasurementUnit.NONE.apiName()); + final MeasurementValue ffValues = + new MeasurementValue(frozenFrames, SentryMeasurementUnit.NONE.apiName()); final Map measurements = new HashMap<>(); measurements.put("frames_total", tfValues); measurements.put("frames_slow", sfValues); diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/PerformanceAndroidEventProcessor.java b/sentry-android-core/src/main/java/io/sentry/android/core/PerformanceAndroidEventProcessor.java index a3fca88156e..9d5b4447536 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/PerformanceAndroidEventProcessor.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/PerformanceAndroidEventProcessor.java @@ -3,11 +3,11 @@ import static io.sentry.android.core.ActivityLifecycleIntegration.APP_START_COLD; import static io.sentry.android.core.ActivityLifecycleIntegration.APP_START_WARM; import static io.sentry.android.core.ActivityLifecycleIntegration.UI_LOAD_OP; -import static io.sentry.protocol.MeasurementValue.MILLISECOND_UNIT; import io.sentry.EventProcessor; import io.sentry.Hint; import io.sentry.SentryEvent; +import io.sentry.SentryMeasurementUnit; import io.sentry.SpanContext; import io.sentry.protocol.MeasurementValue; import io.sentry.protocol.SentryId; @@ -67,7 +67,8 @@ public SentryEvent process(@NotNull SentryEvent event, @NotNull Hint hint) { // if appStartUpInterval is null, metrics are not ready to be sent if (appStartUpInterval != null) { final MeasurementValue value = - new MeasurementValue((float) appStartUpInterval, MILLISECOND_UNIT); + new MeasurementValue( + (float) appStartUpInterval, SentryMeasurementUnit.MILLISECOND.apiName()); final String appStartKey = AppStartState.getInstance().isColdStart() ? "app_start_cold" : "app_start_warm"; diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/PerformanceAndroidEventProcessorTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/PerformanceAndroidEventProcessorTest.kt index df595b6a356..99c197f1e01 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/PerformanceAndroidEventProcessorTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/PerformanceAndroidEventProcessorTest.kt @@ -5,12 +5,12 @@ import com.nhaarman.mockitokotlin2.mock import com.nhaarman.mockitokotlin2.whenever import io.sentry.Hint import io.sentry.IHub +import io.sentry.SentryMeasurementUnit import io.sentry.SentryTracer import io.sentry.TracesSamplingDecision import io.sentry.TransactionContext import io.sentry.android.core.ActivityLifecycleIntegration.UI_LOAD_OP import io.sentry.protocol.MeasurementValue -import io.sentry.protocol.MeasurementValue.MILLISECOND_UNIT import io.sentry.protocol.SentryTransaction import java.util.Date import kotlin.test.BeforeTest @@ -156,7 +156,7 @@ class PerformanceAndroidEventProcessorTest { val tracer = SentryTracer(context, fixture.hub) var tr = SentryTransaction(tracer) - val metrics = mapOf("frames_total" to MeasurementValue(1f, MILLISECOND_UNIT)) + val metrics = mapOf("frames_total" to MeasurementValue(1f, SentryMeasurementUnit.MILLISECOND.apiName())) whenever(fixture.activityFramesTracker.takeMetrics(any())).thenReturn(metrics) tr = sut.process(tr, Hint()) diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index a17ca1471c9..2f7a89269bc 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -483,6 +483,8 @@ public abstract interface class io/sentry/ISpan { public abstract fun isFinished ()Z public abstract fun setData (Ljava/lang/String;Ljava/lang/Object;)V public abstract fun setDescription (Ljava/lang/String;)V + public abstract fun setMeasurement (Ljava/lang/String;F)V + public abstract fun setMeasurement (Ljava/lang/String;FLio/sentry/SentryMeasurementUnit;)V public abstract fun setOperation (Ljava/lang/String;)V public abstract fun setStatus (Lio/sentry/SpanStatus;)V public abstract fun setTag (Ljava/lang/String;Ljava/lang/String;)V @@ -668,6 +670,8 @@ public final class io/sentry/NoOpSpan : io/sentry/ISpan { public fun isFinished ()Z public fun setData (Ljava/lang/String;Ljava/lang/Object;)V public fun setDescription (Ljava/lang/String;)V + public fun setMeasurement (Ljava/lang/String;F)V + public fun setMeasurement (Ljava/lang/String;FLio/sentry/SentryMeasurementUnit;)V public fun setOperation (Ljava/lang/String;)V public fun setStatus (Lio/sentry/SpanStatus;)V public fun setTag (Ljava/lang/String;Ljava/lang/String;)V @@ -703,6 +707,8 @@ public final class io/sentry/NoOpTransaction : io/sentry/ITransaction { public fun scheduleFinish ()V public fun setData (Ljava/lang/String;Ljava/lang/Object;)V public fun setDescription (Ljava/lang/String;)V + public fun setMeasurement (Ljava/lang/String;F)V + public fun setMeasurement (Ljava/lang/String;FLio/sentry/SentryMeasurementUnit;)V public fun setName (Ljava/lang/String;)V public fun setName (Ljava/lang/String;Lio/sentry/protocol/TransactionNameSource;)V public fun setOperation (Ljava/lang/String;)V @@ -1268,6 +1274,21 @@ public final class io/sentry/SentryLevel : java/lang/Enum, io/sentry/JsonSeriali public static fun values ()[Lio/sentry/SentryLevel; } +public final class io/sentry/SentryMeasurementUnit : java/lang/Enum { + public static final field DAY Lio/sentry/SentryMeasurementUnit; + public static final field HOUR Lio/sentry/SentryMeasurementUnit; + public static final field MICROSECOND Lio/sentry/SentryMeasurementUnit; + public static final field MILLISECOND Lio/sentry/SentryMeasurementUnit; + public static final field MINUTE Lio/sentry/SentryMeasurementUnit; + public static final field NANOSECOND Lio/sentry/SentryMeasurementUnit; + public static final field NONE Lio/sentry/SentryMeasurementUnit; + public static final field SECOND Lio/sentry/SentryMeasurementUnit; + public static final field WEEK Lio/sentry/SentryMeasurementUnit; + public fun apiName ()Ljava/lang/String; + public static fun valueOf (Ljava/lang/String;)Lio/sentry/SentryMeasurementUnit; + public static fun values ()[Lio/sentry/SentryMeasurementUnit; +} + public class io/sentry/SentryOptions { public fun ()V public fun addContextTag (Ljava/lang/String;)V @@ -1492,6 +1513,8 @@ public final class io/sentry/SentryTracer : io/sentry/ITransaction { public fun scheduleFinish ()V public fun setData (Ljava/lang/String;Ljava/lang/Object;)V public fun setDescription (Ljava/lang/String;)V + public fun setMeasurement (Ljava/lang/String;F)V + public fun setMeasurement (Ljava/lang/String;FLio/sentry/SentryMeasurementUnit;)V public fun setName (Ljava/lang/String;)V public fun setName (Ljava/lang/String;Lio/sentry/protocol/TransactionNameSource;)V public fun setOperation (Ljava/lang/String;)V @@ -1598,6 +1621,8 @@ public final class io/sentry/Span : io/sentry/ISpan { public fun isSampled ()Ljava/lang/Boolean; public fun setData (Ljava/lang/String;Ljava/lang/Object;)V public fun setDescription (Ljava/lang/String;)V + public fun setMeasurement (Ljava/lang/String;F)V + public fun setMeasurement (Ljava/lang/String;FLio/sentry/SentryMeasurementUnit;)V public fun setOperation (Ljava/lang/String;)V public fun setStatus (Lio/sentry/SpanStatus;)V public fun setTag (Ljava/lang/String;Ljava/lang/String;)V @@ -2444,8 +2469,6 @@ public final class io/sentry/protocol/Gpu$JsonKeys { } public final class io/sentry/protocol/MeasurementValue : io/sentry/JsonSerializable, io/sentry/JsonUnknown { - public static final field MILLISECOND_UNIT Ljava/lang/String; - public static final field NONE_UNIT Ljava/lang/String; public fun (FLjava/lang/String;)V public fun (FLjava/lang/String;Ljava/util/Map;)V public fun getUnit ()Ljava/lang/String; diff --git a/sentry/src/main/java/io/sentry/ISpan.java b/sentry/src/main/java/io/sentry/ISpan.java index 537d72de022..3e250631b76 100644 --- a/sentry/src/main/java/io/sentry/ISpan.java +++ b/sentry/src/main/java/io/sentry/ISpan.java @@ -169,4 +169,27 @@ ISpan startChild( */ @Nullable Object getData(@NotNull String key); + + /** + * Set a measurement with NONE unit. + * + *

NOTE: Setting a measurement with the same name on the same transaction multiple times only + * keeps the last value. + * + * @param name the name of the measurement + * @param value the value of the measurement + */ + void setMeasurement(@NotNull String name, float value); + + /** + * Set a measurement with specific unit. + * + *

NOTE: Setting a measurement with the same name on the same transaction multiple times only + * keeps the last value. + * + * @param name the name of the measurement + * @param value the value of the measurement + * @param unit the unit the value is measured in + */ + void setMeasurement(@NotNull String name, float value, @NotNull SentryMeasurementUnit unit); } diff --git a/sentry/src/main/java/io/sentry/NoOpSpan.java b/sentry/src/main/java/io/sentry/NoOpSpan.java index 21294bf63f1..ab81a02d4da 100644 --- a/sentry/src/main/java/io/sentry/NoOpSpan.java +++ b/sentry/src/main/java/io/sentry/NoOpSpan.java @@ -111,4 +111,11 @@ public void setData(@NotNull String key, @NotNull Object value) {} public @Nullable Object getData(@NotNull String key) { return null; } + + @Override + public void setMeasurement(@NotNull String name, float value) {} + + @Override + public void setMeasurement( + @NotNull String name, float value, @NotNull SentryMeasurementUnit unit) {} } diff --git a/sentry/src/main/java/io/sentry/NoOpTransaction.java b/sentry/src/main/java/io/sentry/NoOpTransaction.java index ed5ac8bc727..8973e4ae81e 100644 --- a/sentry/src/main/java/io/sentry/NoOpTransaction.java +++ b/sentry/src/main/java/io/sentry/NoOpTransaction.java @@ -164,4 +164,11 @@ public void setData(@NotNull String key, @NotNull Object value) {} public @Nullable Object getData(@NotNull String key) { return null; } + + @Override + public void setMeasurement(@NotNull String name, float value) {} + + @Override + public void setMeasurement( + @NotNull String name, float value, @NotNull SentryMeasurementUnit unit) {} } diff --git a/sentry/src/main/java/io/sentry/SentryMeasurementUnit.java b/sentry/src/main/java/io/sentry/SentryMeasurementUnit.java new file mode 100644 index 00000000000..8a819f8ead8 --- /dev/null +++ b/sentry/src/main/java/io/sentry/SentryMeasurementUnit.java @@ -0,0 +1,37 @@ +package io.sentry; + +import java.util.Locale; +import org.jetbrains.annotations.NotNull; + +public enum SentryMeasurementUnit { + /** Nanosecond (`"nanosecond"`), 10^-9 seconds. */ + NANOSECOND, + + /** Microsecond (`"microsecond"`), 10^-6 seconds. */ + MICROSECOND, + + /** Millisecond (`"millisecond"`), 10^-3 seconds. */ + MILLISECOND, + + /** Full second (`"second"`). */ + SECOND, + + /** Minute (`"minute"`), 60 seconds. */ + MINUTE, + + /** Hour (`"hour"`), 3600 seconds. */ + HOUR, + + /** Day (`"day"`), 86,400 seconds. */ + DAY, + + /** Week (`"week"`), 604,800 seconds. */ + WEEK, + + /** Untyped value without a unit. */ + NONE; + + public @NotNull String apiName() { + return name().toLowerCase(Locale.ROOT); + } +} diff --git a/sentry/src/main/java/io/sentry/SentryTracer.java b/sentry/src/main/java/io/sentry/SentryTracer.java index c40a74ecd71..d25ad9106b6 100644 --- a/sentry/src/main/java/io/sentry/SentryTracer.java +++ b/sentry/src/main/java/io/sentry/SentryTracer.java @@ -1,5 +1,6 @@ package io.sentry; +import io.sentry.protocol.MeasurementValue; import io.sentry.protocol.SentryId; import io.sentry.protocol.SentryTransaction; import io.sentry.protocol.TransactionNameSource; @@ -13,6 +14,7 @@ import java.util.Map; import java.util.Timer; import java.util.TimerTask; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; @@ -74,6 +76,7 @@ public final class SentryTracer implements ITransaction { private final @NotNull Baggage baggage; private @NotNull TransactionNameSource transactionNameSource; + private final @NotNull Map measurements; public SentryTracer(final @NotNull TransactionContext context, final @NotNull IHub hub) { this(context, hub, null); @@ -104,6 +107,7 @@ public SentryTracer( final @Nullable TransactionFinishedCallback transactionFinishedCallback) { Objects.requireNonNull(context, "context is required"); Objects.requireNonNull(hub, "hub is required"); + this.measurements = new ConcurrentHashMap<>(); this.root = new Span(context, this, hub, startTimestamp); this.name = context.getName(); this.hub = hub; @@ -360,6 +364,9 @@ public void finish(@Nullable SpanStatus status) { // if it's an idle transaction which has no children, we drop it to save user's quota return; } + + transaction.getMeasurements().putAll(measurements); + hub.captureTransaction(transaction, traceContext(), null, profilingTraceData); } } @@ -506,6 +513,17 @@ public void setData(@NotNull String key, @NotNull Object value) { return this.root.getData(key); } + @Override + public void setMeasurement(@NotNull String name, float value) { + this.measurements.put(name, new MeasurementValue(value, SentryMeasurementUnit.NONE.apiName())); + } + + @Override + public void setMeasurement( + @NotNull String name, float value, @NotNull SentryMeasurementUnit unit) { + this.measurements.put(name, new MeasurementValue(value, unit.apiName())); + } + public @Nullable Map getData() { return this.root.getData(); } diff --git a/sentry/src/main/java/io/sentry/Span.java b/sentry/src/main/java/io/sentry/Span.java index 8ae6d0cbb06..f7dd49d6853 100644 --- a/sentry/src/main/java/io/sentry/Span.java +++ b/sentry/src/main/java/io/sentry/Span.java @@ -313,6 +313,17 @@ public void setData(@NotNull String key, @NotNull Object value) { return data.get(key); } + @Override + public void setMeasurement(@NotNull String name, float value) { + this.transaction.setMeasurement(name, value); + } + + @Override + public void setMeasurement( + @NotNull String name, float value, @NotNull SentryMeasurementUnit unit) { + this.transaction.setMeasurement(name, value, unit); + } + @Nullable Long getEndNanos() { return endNanos; diff --git a/sentry/src/main/java/io/sentry/protocol/MeasurementValue.java b/sentry/src/main/java/io/sentry/protocol/MeasurementValue.java index c80b3a47acc..99fafe3125c 100644 --- a/sentry/src/main/java/io/sentry/protocol/MeasurementValue.java +++ b/sentry/src/main/java/io/sentry/protocol/MeasurementValue.java @@ -18,9 +18,6 @@ @ApiStatus.Internal public final class MeasurementValue implements JsonUnknown, JsonSerializable { - public static final @NotNull String NONE_UNIT = "none"; - public static final @NotNull String MILLISECOND_UNIT = "millisecond"; - @SuppressWarnings("UnusedVariable") private final float value; From dd3a1833fd1c73133e17458fc3120f69977afb55 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Wed, 28 Sep 2022 11:54:54 +0200 Subject: [PATCH 2/6] Changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b8ae0af67e..58b41ec5b76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Make user segment a top level property ([#2257](https://github.com/getsentry/sentry-java/pull/2257)) - Replace user `other` with `data` ([#2258](https://github.com/getsentry/sentry-java/pull/2258)) +- Provide API for attaching custom measurements to transactions ([#2260](https://github.com/getsentry/sentry-java/pull/2260)) ## 6.5.0-beta.1 From 0d10f60a8650dfc008a49a912d107ba02939191d Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Wed, 28 Sep 2022 20:23:39 +0200 Subject: [PATCH 3/6] Add tests --- .../src/main/java/io/sentry/SentryTracer.java | 14 ++++++++++ .../io/sentry/SentryMeasurementUnitTest.kt | 20 ++++++++++++++ .../test/java/io/sentry/SentryTracerTest.kt | 27 +++++++++++++++++-- 3 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 sentry/src/test/java/io/sentry/SentryMeasurementUnitTest.kt diff --git a/sentry/src/main/java/io/sentry/SentryTracer.java b/sentry/src/main/java/io/sentry/SentryTracer.java index d25ad9106b6..42f7687a5be 100644 --- a/sentry/src/main/java/io/sentry/SentryTracer.java +++ b/sentry/src/main/java/io/sentry/SentryTracer.java @@ -515,12 +515,20 @@ public void setData(@NotNull String key, @NotNull Object value) { @Override public void setMeasurement(@NotNull String name, float value) { + if (root.isFinished()) { + return; + } + this.measurements.put(name, new MeasurementValue(value, SentryMeasurementUnit.NONE.apiName())); } @Override public void setMeasurement( @NotNull String name, float value, @NotNull SentryMeasurementUnit unit) { + if (root.isFinished()) { + return; + } + this.measurements.put(name, new MeasurementValue(value, unit.apiName())); } @@ -619,6 +627,12 @@ AtomicBoolean isFinishTimerRunning() { return isFinishTimerRunning; } + @TestOnly + @NotNull + Map getMeasurements() { + return measurements; + } + private static final class FinishStatus { static final FinishStatus NOT_FINISHED = FinishStatus.notFinished(); diff --git a/sentry/src/test/java/io/sentry/SentryMeasurementUnitTest.kt b/sentry/src/test/java/io/sentry/SentryMeasurementUnitTest.kt new file mode 100644 index 00000000000..19487e28274 --- /dev/null +++ b/sentry/src/test/java/io/sentry/SentryMeasurementUnitTest.kt @@ -0,0 +1,20 @@ +package io.sentry + +import kotlin.test.Test +import kotlin.test.assertEquals + +class SentryMeasurementUnitTest { + + @Test + fun `apiName converts enum to lowercase`() { + assertEquals("nanosecond", SentryMeasurementUnit.NANOSECOND.apiName()) + assertEquals("microsecond", SentryMeasurementUnit.MICROSECOND.apiName()) + assertEquals("millisecond", SentryMeasurementUnit.MILLISECOND.apiName()) + assertEquals("second", SentryMeasurementUnit.SECOND.apiName()) + assertEquals("minute", SentryMeasurementUnit.MINUTE.apiName()) + assertEquals("hour", SentryMeasurementUnit.HOUR.apiName()) + assertEquals("day", SentryMeasurementUnit.DAY.apiName()) + assertEquals("week", SentryMeasurementUnit.WEEK.apiName()) + assertEquals("none", SentryMeasurementUnit.NONE.apiName()) + } +} diff --git a/sentry/src/test/java/io/sentry/SentryTracerTest.kt b/sentry/src/test/java/io/sentry/SentryTracerTest.kt index db3e7b36b37..5bb39687c14 100644 --- a/sentry/src/test/java/io/sentry/SentryTracerTest.kt +++ b/sentry/src/test/java/io/sentry/SentryTracerTest.kt @@ -8,6 +8,7 @@ import com.nhaarman.mockitokotlin2.never import com.nhaarman.mockitokotlin2.spy import com.nhaarman.mockitokotlin2.times import com.nhaarman.mockitokotlin2.verify +import io.sentry.SentryMeasurementUnit.DAY import io.sentry.protocol.User import org.awaitility.kotlin.await import java.util.Date @@ -367,6 +368,7 @@ class SentryTracerTest { transaction.description = "desc" transaction.setTag("myTag", "myValue") transaction.setData("myData", "myValue") + transaction.setMeasurement("myMetric", 1.0f) val ex = RuntimeException() transaction.throwable = ex @@ -382,13 +384,14 @@ class SentryTracerTest { transaction.setTag("myTag", "myNewValue") transaction.throwable = RuntimeException() transaction.setData("myData", "myNewValue") - transaction.name = "newName" + transaction.setMeasurement("myMetric", 2.0f) assertEquals(SpanStatus.OK, transaction.status) assertEquals("op", transaction.operation) assertEquals("desc", transaction.description) assertEquals("myValue", transaction.getTag("myTag")) assertEquals("myValue", transaction.getData("myData")) + assertEquals(1.0f, transaction.measurements["myMetric"]!!.value) assertEquals("name", transaction.name) assertEquals(ex, transaction.throwable) } @@ -708,7 +711,7 @@ class SentryTracerTest { @Test fun `when idle transaction with children, finishes the transaction after the idle timeout`() { - val transaction = fixture.getSut(waitForChildren = true, idleTimeout = 10) + val transaction = fixture.getSut(waitForChildren = true, idleTimeout = 1000) val span = transaction.startChild("op") span.finish() @@ -804,4 +807,24 @@ class SentryTracerTest { transaction.finish(SpanStatus.OK) assertNull(transaction.timer) } + + @Test + fun `when tracer is finished, puts custom measurements into underlying transaction`() { + val transaction = fixture.getSut() + transaction.setMeasurement("metric1", 1.0f) + transaction.setMeasurement("days", 2f, DAY) + transaction.finish() + + verify(fixture.hub).captureTransaction( + check { + assertEquals(1.0f, it.measurements["metric1"]!!.value) + + assertEquals(2f, it.measurements["days"]!!.value) + assertEquals("day", it.measurements["days"]!!.unit) + }, + anyOrNull(), + anyOrNull(), + anyOrNull(), + ) + } } From 018e0db898a74377397ba8230b0e59502515947a Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Wed, 28 Sep 2022 20:27:08 +0200 Subject: [PATCH 4/6] Update sentry/src/test/java/io/sentry/SentryTracerTest.kt --- sentry/src/test/java/io/sentry/SentryTracerTest.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/sentry/src/test/java/io/sentry/SentryTracerTest.kt b/sentry/src/test/java/io/sentry/SentryTracerTest.kt index 5bb39687c14..8c1fe0f8181 100644 --- a/sentry/src/test/java/io/sentry/SentryTracerTest.kt +++ b/sentry/src/test/java/io/sentry/SentryTracerTest.kt @@ -384,6 +384,7 @@ class SentryTracerTest { transaction.setTag("myTag", "myNewValue") transaction.throwable = RuntimeException() transaction.setData("myData", "myNewValue") + transaction.name = "newName" transaction.setMeasurement("myMetric", 2.0f) assertEquals(SpanStatus.OK, transaction.status) From f765bcf06ce5a5deeb5c7bf991a133081493113b Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Wed, 28 Sep 2022 20:49:21 +0200 Subject: [PATCH 5/6] Add example custom metrics to samples --- .../java/io/sentry/samples/android/MainActivity.java | 3 +++ .../io/sentry/samples/spring/boot/PersonService.java | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/MainActivity.java b/sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/MainActivity.java index b337973bcf8..dc328720f43 100644 --- a/sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/MainActivity.java +++ b/sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/MainActivity.java @@ -27,6 +27,7 @@ public class MainActivity extends AppCompatActivity { private int crashCount = 0; + private int screenLoadCount = 0; @Override protected void onCreate(Bundle savedInstanceState) { @@ -200,8 +201,10 @@ protected void onCreate(Bundle savedInstanceState) { @Override protected void onResume() { super.onResume(); + screenLoadCount++; final ISpan span = Sentry.getSpan(); if (span != null) { + span.setMeasurement("screen_load_count", screenLoadCount); span.finish(SpanStatus.OK); } } diff --git a/sentry-samples/sentry-samples-spring-boot/src/main/java/io/sentry/samples/spring/boot/PersonService.java b/sentry-samples/sentry-samples-spring-boot/src/main/java/io/sentry/samples/spring/boot/PersonService.java index 76d528f585c..0eb67e85788 100644 --- a/sentry-samples/sentry-samples-spring-boot/src/main/java/io/sentry/samples/spring/boot/PersonService.java +++ b/sentry-samples/sentry-samples-spring-boot/src/main/java/io/sentry/samples/spring/boot/PersonService.java @@ -1,5 +1,7 @@ package io.sentry.samples.spring.boot; +import io.sentry.ISpan; +import io.sentry.Sentry; import io.sentry.spring.tracing.SentrySpan; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -16,12 +18,19 @@ public class PersonService { private static final Logger LOGGER = LoggerFactory.getLogger(PersonService.class); private final JdbcTemplate jdbcTemplate; + private int createCount = 0; public PersonService(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } Person create(Person person) { + createCount++; + final ISpan span = Sentry.getSpan(); + if (span != null) { + span.setMeasurement("create_count", createCount); + } + jdbcTemplate.update( "insert into person (firstName, lastName) values (?, ?)", person.getFirstName(), From 2d60a976ff31ae402142310f4e46dcea5e87a68f Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Thu, 29 Sep 2022 12:57:31 +0200 Subject: [PATCH 6/6] Address PR reviews --- .../src/main/java/io/sentry/samples/android/MainActivity.java | 3 ++- sentry/src/main/java/io/sentry/ISpan.java | 2 +- sentry/src/main/java/io/sentry/SentryMeasurementUnit.java | 3 +++ sentry/src/main/java/io/sentry/SentryTracer.java | 2 +- sentry/src/test/java/io/sentry/SentryTracerTest.kt | 1 + 5 files changed, 8 insertions(+), 3 deletions(-) diff --git a/sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/MainActivity.java b/sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/MainActivity.java index dc328720f43..e08c851cb77 100644 --- a/sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/MainActivity.java +++ b/sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/MainActivity.java @@ -6,6 +6,7 @@ import io.sentry.Attachment; import io.sentry.ISpan; import io.sentry.Sentry; +import io.sentry.SentryMeasurementUnit; import io.sentry.SpanStatus; import io.sentry.UserFeedback; import io.sentry.protocol.SentryId; @@ -204,7 +205,7 @@ protected void onResume() { screenLoadCount++; final ISpan span = Sentry.getSpan(); if (span != null) { - span.setMeasurement("screen_load_count", screenLoadCount); + span.setMeasurement("screen_load_count", screenLoadCount, SentryMeasurementUnit.DAY); span.finish(SpanStatus.OK); } } diff --git a/sentry/src/main/java/io/sentry/ISpan.java b/sentry/src/main/java/io/sentry/ISpan.java index 3e250631b76..023f636b526 100644 --- a/sentry/src/main/java/io/sentry/ISpan.java +++ b/sentry/src/main/java/io/sentry/ISpan.java @@ -171,7 +171,7 @@ ISpan startChild( Object getData(@NotNull String key); /** - * Set a measurement with NONE unit. + * Set a measurement without unit. * *

NOTE: Setting a measurement with the same name on the same transaction multiple times only * keeps the last value. diff --git a/sentry/src/main/java/io/sentry/SentryMeasurementUnit.java b/sentry/src/main/java/io/sentry/SentryMeasurementUnit.java index 8a819f8ead8..3e981657b8e 100644 --- a/sentry/src/main/java/io/sentry/SentryMeasurementUnit.java +++ b/sentry/src/main/java/io/sentry/SentryMeasurementUnit.java @@ -3,6 +3,9 @@ import java.util.Locale; import org.jetbrains.annotations.NotNull; +/** + * Develop Docs + */ public enum SentryMeasurementUnit { /** Nanosecond (`"nanosecond"`), 10^-9 seconds. */ NANOSECOND, diff --git a/sentry/src/main/java/io/sentry/SentryTracer.java b/sentry/src/main/java/io/sentry/SentryTracer.java index 42f7687a5be..71fbef617d4 100644 --- a/sentry/src/main/java/io/sentry/SentryTracer.java +++ b/sentry/src/main/java/io/sentry/SentryTracer.java @@ -519,7 +519,7 @@ public void setMeasurement(@NotNull String name, float value) { return; } - this.measurements.put(name, new MeasurementValue(value, SentryMeasurementUnit.NONE.apiName())); + this.measurements.put(name, new MeasurementValue(value, null)); } @Override diff --git a/sentry/src/test/java/io/sentry/SentryTracerTest.kt b/sentry/src/test/java/io/sentry/SentryTracerTest.kt index 8c1fe0f8181..419ba494210 100644 --- a/sentry/src/test/java/io/sentry/SentryTracerTest.kt +++ b/sentry/src/test/java/io/sentry/SentryTracerTest.kt @@ -819,6 +819,7 @@ class SentryTracerTest { verify(fixture.hub).captureTransaction( check { assertEquals(1.0f, it.measurements["metric1"]!!.value) + assertEquals(null, it.measurements["metric1"]!!.unit) assertEquals(2f, it.measurements["days"]!!.value) assertEquals("day", it.measurements["days"]!!.unit)