Skip to content

Commit 268afc3

Browse files
authored
Merge afde400 into 9578eab
2 parents 9578eab + afde400 commit 268afc3

19 files changed

+266
-31
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
### Features
66

7+
- Add support for measurements at span level ([#3219](https://github.com/getsentry/sentry-java/pull/3219))
78
- Add `enableScopePersistence` option to disable `PersistingScopeObserver` used for ANR reporting which may increase performance overhead. Defaults to `true` ([#3218](https://github.com/getsentry/sentry-java/pull/3218))
89
- When disabled, the SDK will not enrich ANRv2 events with scope data (e.g. breadcrumbs, user, tags, etc.)
910

sentry-android-core/src/main/java/io/sentry/android/core/PerformanceAndroidEventProcessor.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.util.HashMap;
2525
import java.util.List;
2626
import java.util.Map;
27+
import java.util.concurrent.ConcurrentHashMap;
2728
import org.jetbrains.annotations.NotNull;
2829
import org.jetbrains.annotations.Nullable;
2930

@@ -248,7 +249,8 @@ private static SentrySpan timeSpanToSentrySpan(
248249
span.getDescription(),
249250
SpanStatus.OK,
250251
APP_METRICS_ORIGIN,
251-
new HashMap<>(),
252+
new ConcurrentHashMap<>(),
253+
new ConcurrentHashMap<>(),
252254
defaultSpanData);
253255
}
254256
}

sentry-android-core/src/test/java/io/sentry/android/core/PerformanceAndroidEventProcessorTest.kt

+4
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ class PerformanceAndroidEventProcessorTest {
242242
SpanStatus.OK,
243243
null,
244244
emptyMap(),
245+
emptyMap(),
245246
null
246247
)
247248
tr.spans.add(appStartSpan)
@@ -337,6 +338,7 @@ class PerformanceAndroidEventProcessorTest {
337338
SpanStatus.OK,
338339
null,
339340
emptyMap(),
341+
emptyMap(),
340342
null
341343
)
342344
tr.spans.add(appStartSpan)
@@ -386,6 +388,7 @@ class PerformanceAndroidEventProcessorTest {
386388
SpanStatus.OK,
387389
null,
388390
emptyMap(),
391+
emptyMap(),
389392
null
390393
)
391394
tr.spans.add(appStartSpan)
@@ -431,6 +434,7 @@ class PerformanceAndroidEventProcessorTest {
431434
SpanStatus.OK,
432435
null,
433436
emptyMap(),
437+
emptyMap(),
434438
null
435439
)
436440
tr.spans.add(appStartSpan)

sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/MainActivity.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,10 @@ protected void onResume() {
267267
screenLoadCount++;
268268
final ISpan span = Sentry.getSpan();
269269
if (span != null) {
270-
span.setMeasurement("screen_load_count", screenLoadCount, new MeasurementUnit.Custom("test"));
270+
ISpan measurementSpan = span.startChild("screen_load_measurement", "test measurement");
271+
measurementSpan.setMeasurement(
272+
"screen_load_count", screenLoadCount, new MeasurementUnit.Custom("test"));
273+
measurementSpan.finish();
271274
}
272275
Sentry.reportFullyDisplayed();
273276
}

sentry/api/sentry.api

+6-1
Original file line numberDiff line numberDiff line change
@@ -2463,6 +2463,8 @@ public final class io/sentry/SentryTracer : io/sentry/ITransaction {
24632463
public fun setDescription (Ljava/lang/String;)V
24642464
public fun setMeasurement (Ljava/lang/String;Ljava/lang/Number;)V
24652465
public fun setMeasurement (Ljava/lang/String;Ljava/lang/Number;Lio/sentry/MeasurementUnit;)V
2466+
public fun setMeasurementFromChild (Ljava/lang/String;Ljava/lang/Number;)V
2467+
public fun setMeasurementFromChild (Ljava/lang/String;Ljava/lang/Number;Lio/sentry/MeasurementUnit;)V
24662468
public fun setName (Ljava/lang/String;)V
24672469
public fun setName (Ljava/lang/String;Lio/sentry/protocol/TransactionNameSource;)V
24682470
public fun setOperation (Ljava/lang/String;)V
@@ -2567,6 +2569,7 @@ public final class io/sentry/Span : io/sentry/ISpan {
25672569
public fun getData (Ljava/lang/String;)Ljava/lang/Object;
25682570
public fun getDescription ()Ljava/lang/String;
25692571
public fun getFinishDate ()Lio/sentry/SentryDate;
2572+
public fun getMeasurements ()Ljava/util/Map;
25702573
public fun getOperation ()Ljava/lang/String;
25712574
public fun getParentSpanId ()Lio/sentry/SpanId;
25722575
public fun getSamplingDecision ()Lio/sentry/TracesSamplingDecision;
@@ -4187,9 +4190,10 @@ public final class io/sentry/protocol/SentryRuntime$JsonKeys {
41874190
public final class io/sentry/protocol/SentrySpan : io/sentry/JsonSerializable, io/sentry/JsonUnknown {
41884191
public fun <init> (Lio/sentry/Span;)V
41894192
public fun <init> (Lio/sentry/Span;Ljava/util/Map;)V
4190-
public fun <init> (Ljava/lang/Double;Ljava/lang/Double;Lio/sentry/protocol/SentryId;Lio/sentry/SpanId;Lio/sentry/SpanId;Ljava/lang/String;Ljava/lang/String;Lio/sentry/SpanStatus;Ljava/lang/String;Ljava/util/Map;Ljava/util/Map;)V
4193+
public fun <init> (Ljava/lang/Double;Ljava/lang/Double;Lio/sentry/protocol/SentryId;Lio/sentry/SpanId;Lio/sentry/SpanId;Ljava/lang/String;Ljava/lang/String;Lio/sentry/SpanStatus;Ljava/lang/String;Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;)V
41914194
public fun getData ()Ljava/util/Map;
41924195
public fun getDescription ()Ljava/lang/String;
4196+
public fun getMeasurements ()Ljava/util/Map;
41934197
public fun getOp ()Ljava/lang/String;
41944198
public fun getOrigin ()Ljava/lang/String;
41954199
public fun getParentSpanId ()Lio/sentry/SpanId;
@@ -4214,6 +4218,7 @@ public final class io/sentry/protocol/SentrySpan$Deserializer : io/sentry/JsonDe
42144218
public final class io/sentry/protocol/SentrySpan$JsonKeys {
42154219
public static final field DATA Ljava/lang/String;
42164220
public static final field DESCRIPTION Ljava/lang/String;
4221+
public static final field MEASUREMENTS Ljava/lang/String;
42174222
public static final field OP Ljava/lang/String;
42184223
public static final field ORIGIN Ljava/lang/String;
42194224
public static final field PARENT_SPAN_ID Ljava/lang/String;

sentry/src/main/java/io/sentry/SentryTracer.java

+56-21
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package io.sentry;
22

33
import io.sentry.protocol.Contexts;
4-
import io.sentry.protocol.MeasurementValue;
54
import io.sentry.protocol.SentryId;
65
import io.sentry.protocol.SentryTransaction;
76
import io.sentry.protocol.TransactionNameSource;
@@ -13,7 +12,6 @@
1312
import java.util.Map;
1413
import java.util.Timer;
1514
import java.util.TimerTask;
16-
import java.util.concurrent.ConcurrentHashMap;
1715
import java.util.concurrent.CopyOnWriteArrayList;
1816
import java.util.concurrent.atomic.AtomicBoolean;
1917
import java.util.concurrent.atomic.AtomicReference;
@@ -48,7 +46,6 @@ public final class SentryTracer implements ITransaction {
4846

4947
private final @NotNull Baggage baggage;
5048
private @NotNull TransactionNameSource transactionNameSource;
51-
private final @NotNull Map<String, MeasurementValue> measurements;
5249
private final @NotNull Instrumenter instrumenter;
5350
private final @NotNull Contexts contexts = new Contexts();
5451
private final @Nullable TransactionPerformanceCollector transactionPerformanceCollector;
@@ -72,7 +69,6 @@ public SentryTracer(
7269
final @Nullable TransactionPerformanceCollector transactionPerformanceCollector) {
7370
Objects.requireNonNull(context, "context is required");
7471
Objects.requireNonNull(hub, "hub is required");
75-
this.measurements = new ConcurrentHashMap<>();
7672

7773
this.root =
7874
new Span(context, this, hub, transactionOptions.getStartTimestamp(), transactionOptions);
@@ -263,7 +259,7 @@ public void finish(
263259
return;
264260
}
265261

266-
transaction.getMeasurements().putAll(measurements);
262+
transaction.getMeasurements().putAll(root.getMeasurements());
267263
hub.captureTransaction(transaction, traceContext(), hint, profilingTraceData);
268264
}
269265
}
@@ -619,6 +615,12 @@ private boolean hasAllChildrenFinished() {
619615
@Override
620616
public void setOperation(final @NotNull String operation) {
621617
if (root.isFinished()) {
618+
hub.getOptions()
619+
.getLogger()
620+
.log(
621+
SentryLevel.DEBUG,
622+
"The transaction is already finished. Operation %s cannot be set",
623+
operation);
622624
return;
623625
}
624626

@@ -633,6 +635,12 @@ public void setOperation(final @NotNull String operation) {
633635
@Override
634636
public void setDescription(final @Nullable String description) {
635637
if (root.isFinished()) {
638+
hub.getOptions()
639+
.getLogger()
640+
.log(
641+
SentryLevel.DEBUG,
642+
"The transaction is already finished. Description %s cannot be set",
643+
description);
636644
return;
637645
}
638646

@@ -647,6 +655,12 @@ public void setDescription(final @Nullable String description) {
647655
@Override
648656
public void setStatus(final @Nullable SpanStatus status) {
649657
if (root.isFinished()) {
658+
hub.getOptions()
659+
.getLogger()
660+
.log(
661+
SentryLevel.DEBUG,
662+
"The transaction is already finished. Status %s cannot be set",
663+
status == null ? "null" : status.name());
650664
return;
651665
}
652666

@@ -661,6 +675,9 @@ public void setStatus(final @Nullable SpanStatus status) {
661675
@Override
662676
public void setThrowable(final @Nullable Throwable throwable) {
663677
if (root.isFinished()) {
678+
hub.getOptions()
679+
.getLogger()
680+
.log(SentryLevel.DEBUG, "The transaction is already finished. Throwable cannot be set");
664681
return;
665682
}
666683

@@ -680,6 +697,9 @@ public void setThrowable(final @Nullable Throwable throwable) {
680697
@Override
681698
public void setTag(final @NotNull String key, final @NotNull String value) {
682699
if (root.isFinished()) {
700+
hub.getOptions()
701+
.getLogger()
702+
.log(SentryLevel.DEBUG, "The transaction is already finished. Tag %s cannot be set", key);
683703
return;
684704
}
685705

@@ -699,6 +719,10 @@ public boolean isFinished() {
699719
@Override
700720
public void setData(@NotNull String key, @NotNull Object value) {
701721
if (root.isFinished()) {
722+
hub.getOptions()
723+
.getLogger()
724+
.log(
725+
SentryLevel.DEBUG, "The transaction is already finished. Data %s cannot be set", key);
702726
return;
703727
}
704728

@@ -710,25 +734,36 @@ public void setData(@NotNull String key, @NotNull Object value) {
710734
return this.root.getData(key);
711735
}
712736

713-
@Override
714-
public void setMeasurement(final @NotNull String name, final @NotNull Number value) {
715-
if (root.isFinished()) {
716-
return;
737+
@ApiStatus.Internal
738+
public void setMeasurementFromChild(final @NotNull String name, final @NotNull Number value) {
739+
// We don't want to overwrite the root span measurement, if it comes from a child.
740+
if (!root.getMeasurements().containsKey(name)) {
741+
setMeasurement(name, value);
717742
}
743+
}
718744

719-
this.measurements.put(name, new MeasurementValue(value, null));
745+
@ApiStatus.Internal
746+
public void setMeasurementFromChild(
747+
final @NotNull String name,
748+
final @NotNull Number value,
749+
final @NotNull MeasurementUnit unit) {
750+
// We don't want to overwrite the root span measurement, if it comes from a child.
751+
if (!root.getMeasurements().containsKey(name)) {
752+
setMeasurement(name, value, unit);
753+
}
754+
}
755+
756+
@Override
757+
public void setMeasurement(final @NotNull String name, final @NotNull Number value) {
758+
root.setMeasurement(name, value);
720759
}
721760

722761
@Override
723762
public void setMeasurement(
724763
final @NotNull String name,
725764
final @NotNull Number value,
726765
final @NotNull MeasurementUnit unit) {
727-
if (root.isFinished()) {
728-
return;
729-
}
730-
731-
this.measurements.put(name, new MeasurementValue(value, unit.apiName()));
766+
root.setMeasurement(name, value, unit);
732767
}
733768

734769
public @Nullable Map<String, Object> getData() {
@@ -759,6 +794,12 @@ public void setName(@NotNull String name) {
759794
@Override
760795
public void setName(@NotNull String name, @NotNull TransactionNameSource transactionNameSource) {
761796
if (root.isFinished()) {
797+
hub.getOptions()
798+
.getLogger()
799+
.log(
800+
SentryLevel.DEBUG,
801+
"The transaction is already finished. Name %s cannot be set",
802+
name);
762803
return;
763804
}
764805

@@ -834,12 +875,6 @@ AtomicBoolean isDeadlineTimerRunning() {
834875
return isDeadlineTimerRunning;
835876
}
836877

837-
@TestOnly
838-
@NotNull
839-
Map<String, MeasurementValue> getMeasurements() {
840-
return measurements;
841-
}
842-
843878
@ApiStatus.Internal
844879
@Override
845880
public void setContext(final @NotNull String key, final @NotNull Object context) {

sentry/src/main/java/io/sentry/Span.java

+43-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.sentry;
22

3+
import io.sentry.protocol.MeasurementValue;
34
import io.sentry.protocol.SentryId;
45
import io.sentry.util.Objects;
56
import java.util.ArrayList;
@@ -41,6 +42,7 @@ public final class Span implements ISpan {
4142
private @Nullable SpanFinishedCallback spanFinishedCallback;
4243

4344
private final @NotNull Map<String, Object> data = new ConcurrentHashMap<>();
45+
private final @NotNull Map<String, MeasurementValue> measurements = new ConcurrentHashMap<>();
4446

4547
Span(
4648
final @NotNull SentryId traceId,
@@ -323,24 +325,59 @@ public Map<String, String> getTags() {
323325
}
324326

325327
@Override
326-
public void setData(@NotNull String key, @NotNull Object value) {
328+
public void setData(final @NotNull String key, final @NotNull Object value) {
327329
data.put(key, value);
328330
}
329331

330332
@Override
331-
public @Nullable Object getData(@NotNull String key) {
333+
public @Nullable Object getData(final @NotNull String key) {
332334
return data.get(key);
333335
}
334336

335337
@Override
336-
public void setMeasurement(@NotNull String name, @NotNull Number value) {
337-
this.transaction.setMeasurement(name, value);
338+
public void setMeasurement(final @NotNull String name, final @NotNull Number value) {
339+
if (isFinished()) {
340+
hub.getOptions()
341+
.getLogger()
342+
.log(
343+
SentryLevel.DEBUG,
344+
"The span is already finished. Measurement %s cannot be set",
345+
name);
346+
return;
347+
}
348+
this.measurements.put(name, new MeasurementValue(value, null));
349+
// We set the measurement in the transaction, too, but we have to check if this is the root span
350+
// of the transaction, to avoid an infinite recursion
351+
if (transaction.getRoot() != this) {
352+
transaction.setMeasurementFromChild(name, value);
353+
}
338354
}
339355

340356
@Override
341357
public void setMeasurement(
342-
@NotNull String name, @NotNull Number value, @NotNull MeasurementUnit unit) {
343-
this.transaction.setMeasurement(name, value, unit);
358+
final @NotNull String name,
359+
final @NotNull Number value,
360+
final @NotNull MeasurementUnit unit) {
361+
if (isFinished()) {
362+
hub.getOptions()
363+
.getLogger()
364+
.log(
365+
SentryLevel.DEBUG,
366+
"The span is already finished. Measurement %s cannot be set",
367+
name);
368+
return;
369+
}
370+
this.measurements.put(name, new MeasurementValue(value, unit.apiName()));
371+
// We set the measurement in the transaction, too, but we have to check if this is the root span
372+
// of the transaction, to avoid an infinite recursion
373+
if (transaction.getRoot() != this) {
374+
transaction.setMeasurementFromChild(name, value, unit);
375+
}
376+
}
377+
378+
@NotNull
379+
public Map<String, MeasurementValue> getMeasurements() {
380+
return measurements;
344381
}
345382

346383
@Override

0 commit comments

Comments
 (0)