Skip to content

Commit 182cda4

Browse files
authored
Merge branch 'main' into feat/pass-through-unknown-sentry-baggage-keys
2 parents 8184db7 + d390819 commit 182cda4

19 files changed

Lines changed: 420 additions & 40 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44

55
### Features
66

7+
- Add `name` and `geo` to `User` ([#2556](https://github.com/getsentry/sentry-java/pull/2556))
78
- Add time-to-initial-display and time-to-full-display measurements to Activity transactions ([#2611](https://github.com/getsentry/sentry-java/pull/2611))
89
- Read integration list written by sentry gradle plugin from manifest ([#2598](https://github.com/getsentry/sentry-java/pull/2598))
910

1011
### Fixes
1112

13+
- Fix Automatic UI transactions having wrong durations ([#2623](https://github.com/getsentry/sentry-java/pull/2623))
1214
- Fix wrong default environment in Session ([#2610](https://github.com/getsentry/sentry-java/pull/2610))
1315
- Pass through unknown sentry baggage keys into SentryEnvelopeHeader ([#2618](https://github.com/getsentry/sentry-java/pull/2618))
1416

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,9 @@ This directory is also included in `.gitignore` not to be shown as pending chang
9696

9797
# Sentry Self Hosted Compatibility
9898

99-
Since version 3.0.0 of this SDK, Sentry version >= v20.6.0 is required. This only applies to on-premise Sentry, if you are using [sentry.io](http://sentry.io/) no action is needed.
99+
Since version 3.0.0 of this SDK, Sentry version >= v20.6.0 is required. This only applies to self-hosted Sentry, if you are using [sentry.io](http://sentry.io/) no action is needed.
100+
101+
Since version 6.0.0 of this SDK, Sentry version >= v21.9.0 is required or you have to manually disable sending client reports via the `sendClientReports` option. This only applies to self-hosted Sentry, if you are using [sentry.io](http://sentry.io/) no action is needed.
100102

101103
# Resources
102104

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

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -208,18 +208,24 @@ private void startTracing(final @NotNull Activity activity) {
208208
}
209209
});
210210

211+
// This will be the start timestamp of the transaction, as well as the ttid/ttfd spans
212+
final @NotNull SentryDate ttidStartTime;
213+
211214
if (!(firstActivityCreated || appStartTime == null || coldStart == null)) {
212-
transactionOptions.setStartTimestamp(appStartTime);
215+
// The first activity ttid/ttfd spans should start at the app start time
216+
ttidStartTime = appStartTime;
217+
} else {
218+
// The ttid/ttfd spans should start when the previous activity called its onPause method
219+
ttidStartTime = lastPausedTime;
213220
}
221+
transactionOptions.setStartTimestamp(ttidStartTime);
214222

215223
// we can only bind to the scope if there's no running transaction
216224
ITransaction transaction =
217225
hub.startTransaction(
218226
new TransactionContext(activityName, TransactionNameSource.COMPONENT, UI_LOAD_OP),
219227
transactionOptions);
220228

221-
final @NotNull SentryDate ttidStartTime;
222-
223229
// in case appStartTime isn't available, we don't create a span for it.
224230
if (!(firstActivityCreated || appStartTime == null || coldStart == null)) {
225231
// start specific span for app start
@@ -233,12 +239,6 @@ private void startTracing(final @NotNull Activity activity) {
233239
// in case there's already an end time (e.g. due to deferred SDK init)
234240
// we can finish the app-start span
235241
finishAppStartSpan();
236-
237-
// The first activity ttid/ttfd spans should start at the app start time
238-
ttidStartTime = appStartTime;
239-
} else {
240-
// The ttid/ttfd spans should start when the previous activity called its onPause method
241-
ttidStartTime = lastPausedTime;
242242
}
243243
ttidSpanMap.put(
244244
activity,

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

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ import io.sentry.protocol.MeasurementValue
3232
import io.sentry.protocol.TransactionNameSource
3333
import io.sentry.test.getProperty
3434
import org.junit.runner.RunWith
35-
import org.mockito.ArgumentCaptor
3635
import org.mockito.kotlin.any
3736
import org.mockito.kotlin.anyOrNull
37+
import org.mockito.kotlin.argumentCaptor
3838
import org.mockito.kotlin.check
3939
import org.mockito.kotlin.eq
4040
import org.mockito.kotlin.mock
@@ -73,7 +73,9 @@ class ActivityLifecycleIntegrationTest {
7373
val activityFramesTracker = mock<ActivityFramesTracker>()
7474
val fullyDisplayedReporter = FullyDisplayedReporter.getInstance()
7575
val transactionFinishedCallback = mock<TransactionFinishedCallback>()
76-
lateinit var transaction: SentryTracer
76+
77+
// we init the transaction with a mock to avoid errors when finishing it after tests that don't start it
78+
var transaction: SentryTracer = mock()
7779
val buildInfo = mock<BuildInfoProvider>()
7880

7981
fun getSut(
@@ -85,15 +87,13 @@ class ActivityLifecycleIntegrationTest {
8587

8688
whenever(hub.options).thenReturn(options)
8789

88-
// TODO: we should let the ActivityLifecycleIntegration create the proper transaction here
89-
val transactionOptions = TransactionOptions().apply {
90-
isWaitForChildren = true
91-
if (options.isEnableActivityLifecycleTracingAutoFinish) {
92-
idleTimeout = options.idleTimeout
93-
}
90+
// We let the ActivityLifecycleIntegration create the proper transaction here
91+
val argumentCaptor = argumentCaptor<TransactionOptions>()
92+
whenever(hub.startTransaction(any(), argumentCaptor.capture())).thenAnswer {
93+
val t = SentryTracer(context, hub, argumentCaptor.lastValue, transactionFinishedCallback)
94+
transaction = t
95+
return@thenAnswer t
9496
}
95-
transaction = SentryTracer(context, hub, transactionOptions, transactionFinishedCallback)
96-
whenever(hub.startTransaction(any(), any<TransactionOptions>())).thenReturn(transaction)
9797
whenever(buildInfo.sdkInfoVersion).thenReturn(apiVersion)
9898

9999
whenever(application.getSystemService(any())).thenReturn(am)
@@ -842,7 +842,7 @@ class ActivityLifecycleIntegrationTest {
842842
sut.onActivityCreated(activity, fixture.bundle)
843843

844844
// call only once
845-
verify(fixture.hub).startTransaction(any(), check<TransactionOptions> { assertNull(it.startTimestamp) })
845+
verify(fixture.hub).startTransaction(any(), check<TransactionOptions> { assertNotEquals(date, it.startTimestamp) })
846846
}
847847

848848
@Test
@@ -1024,26 +1024,19 @@ class ActivityLifecycleIntegrationTest {
10241024
fixture.options.tracesSampleRate = 1.0
10251025
sut.register(fixture.hub, fixture.options)
10261026

1027-
// Using ArgumentCaptor because multiple verify() throws an assertionError, even if the test passes
1028-
val parameters: ArgumentCaptor<TransactionOptions> = ArgumentCaptor.forClass(TransactionOptions::class.java)
10291027
val date = SentryNanotimeDate(Date(0), 0)
10301028
setAppStartTime()
10311029

10321030
val activity = mock<Activity>()
10331031
// First invocation: we expect to start a transaction with the appStartTime
10341032
sut.onActivityCreated(activity, fixture.bundle)
10351033
sut.onActivityPostResumed(activity)
1034+
assertEquals(date.nanoTimestamp(), fixture.transaction.startDate.nanoTimestamp())
10361035

10371036
val newActivity = mock<Activity>()
1038-
// Second invocation: we expect the transaction start timestamp not to be set
1037+
// Second invocation: we expect to start a transaction with a different start timestamp
10391038
sut.onActivityCreated(newActivity, fixture.bundle)
1040-
1041-
verify(fixture.hub, times(2)).startTransaction(any(), parameters.capture())
1042-
val capturedValues = parameters.allValues
1043-
// The first invocation contains the appStartTime
1044-
assertEquals(date.nanoTimestamp(), capturedValues[0].startTimestamp!!.nanoTimestamp())
1045-
// The second invocation contains no start timestamp
1046-
assertNull(capturedValues[1].startTimestamp)
1039+
assertNotEquals(date.nanoTimestamp(), fixture.transaction.startDate.nanoTimestamp())
10471040
}
10481041

10491042
@Test
@@ -1294,6 +1287,24 @@ class ActivityLifecycleIntegrationTest {
12941287
)
12951288
}
12961289

1290+
@Test
1291+
fun `transaction has same start timestamp of ttid and ttfd`() {
1292+
val sut = fixture.getSut()
1293+
val activity = mock<Activity>()
1294+
fixture.options.tracesSampleRate = 1.0
1295+
fixture.options.isEnableTimeToFullDisplayTracing = true
1296+
1297+
sut.register(fixture.hub, fixture.options)
1298+
sut.onActivityCreated(activity, fixture.bundle)
1299+
1300+
// The ttid span should be running
1301+
val ttidSpan = sut.ttidSpanMap[activity]
1302+
assertNotNull(ttidSpan)
1303+
1304+
assertEquals(ttidSpan.startDate, fixture.transaction.startDate)
1305+
assertEquals(sut.ttfdSpan?.startDate, fixture.transaction.startDate)
1306+
}
1307+
12971308
private fun runFirstDraw(view: View) {
12981309
// Removes OnDrawListener in the next OnGlobalLayout after onDraw
12991310
view.viewTreeObserver.dispatchOnDraw()

sentry/api/sentry.api

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2868,6 +2868,33 @@ public final class io/sentry/protocol/Device$JsonKeys {
28682868
public fun <init> ()V
28692869
}
28702870

2871+
public final class io/sentry/protocol/Geo : io/sentry/JsonSerializable, io/sentry/JsonUnknown {
2872+
public fun <init> ()V
2873+
public fun <init> (Lio/sentry/protocol/Geo;)V
2874+
public fun getCity ()Ljava/lang/String;
2875+
public fun getCountryCode ()Ljava/lang/String;
2876+
public fun getRegion ()Ljava/lang/String;
2877+
public fun getUnknown ()Ljava/util/Map;
2878+
public fun serialize (Lio/sentry/JsonObjectWriter;Lio/sentry/ILogger;)V
2879+
public fun setCity (Ljava/lang/String;)V
2880+
public fun setCountryCode (Ljava/lang/String;)V
2881+
public fun setRegion (Ljava/lang/String;)V
2882+
public fun setUnknown (Ljava/util/Map;)V
2883+
}
2884+
2885+
public final class io/sentry/protocol/Geo$Deserializer : io/sentry/JsonDeserializer {
2886+
public fun <init> ()V
2887+
public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/Geo;
2888+
public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object;
2889+
}
2890+
2891+
public final class io/sentry/protocol/Geo$JsonKeys {
2892+
public static final field CITY Ljava/lang/String;
2893+
public static final field COUNTRY_CODE Ljava/lang/String;
2894+
public static final field REGION Ljava/lang/String;
2895+
public fun <init> ()V
2896+
}
2897+
28712898
public final class io/sentry/protocol/Gpu : io/sentry/JsonSerializable, io/sentry/JsonUnknown {
28722899
public static final field TYPE Ljava/lang/String;
28732900
public fun <init> ()V
@@ -3538,17 +3565,21 @@ public final class io/sentry/protocol/User : io/sentry/JsonSerializable, io/sent
35383565
public fun <init> (Lio/sentry/protocol/User;)V
35393566
public fun getData ()Ljava/util/Map;
35403567
public fun getEmail ()Ljava/lang/String;
3568+
public fun getGeo ()Lio/sentry/protocol/Geo;
35413569
public fun getId ()Ljava/lang/String;
35423570
public fun getIpAddress ()Ljava/lang/String;
3571+
public fun getName ()Ljava/lang/String;
35433572
public fun getOthers ()Ljava/util/Map;
35443573
public fun getSegment ()Ljava/lang/String;
35453574
public fun getUnknown ()Ljava/util/Map;
35463575
public fun getUsername ()Ljava/lang/String;
35473576
public fun serialize (Lio/sentry/JsonObjectWriter;Lio/sentry/ILogger;)V
35483577
public fun setData (Ljava/util/Map;)V
35493578
public fun setEmail (Ljava/lang/String;)V
3579+
public fun setGeo (Lio/sentry/protocol/Geo;)V
35503580
public fun setId (Ljava/lang/String;)V
35513581
public fun setIpAddress (Ljava/lang/String;)V
3582+
public fun setName (Ljava/lang/String;)V
35523583
public fun setOthers (Ljava/util/Map;)V
35533584
public fun setSegment (Ljava/lang/String;)V
35543585
public fun setUnknown (Ljava/util/Map;)V
@@ -3564,8 +3595,10 @@ public final class io/sentry/protocol/User$Deserializer : io/sentry/JsonDeserial
35643595
public final class io/sentry/protocol/User$JsonKeys {
35653596
public static final field DATA Ljava/lang/String;
35663597
public static final field EMAIL Ljava/lang/String;
3598+
public static final field GEO Ljava/lang/String;
35673599
public static final field ID Ljava/lang/String;
35683600
public static final field IP_ADDRESS Ljava/lang/String;
3601+
public static final field NAME Ljava/lang/String;
35693602
public static final field OTHER Ljava/lang/String;
35703603
public static final field SEGMENT Ljava/lang/String;
35713604
public static final field USERNAME Ljava/lang/String;

sentry/src/main/java/io/sentry/JsonSerializer.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import io.sentry.protocol.DebugImage;
1010
import io.sentry.protocol.DebugMeta;
1111
import io.sentry.protocol.Device;
12+
import io.sentry.protocol.Geo;
1213
import io.sentry.protocol.Gpu;
1314
import io.sentry.protocol.MeasurementValue;
1415
import io.sentry.protocol.Mechanism;
@@ -108,6 +109,7 @@ public JsonSerializer(@NotNull SentryOptions options) {
108109
deserializersByClass.put(SpanId.class, new SpanId.Deserializer());
109110
deserializersByClass.put(SpanStatus.class, new SpanStatus.Deserializer());
110111
deserializersByClass.put(User.class, new User.Deserializer());
112+
deserializersByClass.put(Geo.class, new Geo.Deserializer());
111113
deserializersByClass.put(UserFeedback.class, new UserFeedback.Deserializer());
112114
deserializersByClass.put(ClientReport.class, new ClientReport.Deserializer());
113115
deserializersByClass.put(ViewHierarchyNode.class, new ViewHierarchyNode.Deserializer());

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,12 @@ public void finish(final @Nullable SpanStatus status, final @Nullable SentryDate
193193
@Nullable SentryDate minChildStart = null;
194194
@Nullable SentryDate maxChildEnd = null;
195195

196-
final @NotNull List<Span> children = getChildren();
196+
// The root span should be trimmed based on all children, but the other spans, like the
197+
// jetpack composition should be trimmed based on its direct children only
198+
final @NotNull List<Span> children =
199+
transaction.getRoot().getSpanId().equals(getSpanId())
200+
? transaction.getChildren()
201+
: getDirectChildren();
197202
for (final Span child : children) {
198203
if (minChildStart == null || child.getStartDate().isBefore(minChildStart)) {
199204
minChildStart = child.getStartDate();
@@ -389,7 +394,7 @@ SpanOptions getOptions() {
389394
}
390395

391396
@NotNull
392-
private List<Span> getChildren() {
397+
private List<Span> getDirectChildren() {
393398
final List<Span> children = new ArrayList<>();
394399
final Iterator<Span> iterator = transaction.getSpans().iterator();
395400

0 commit comments

Comments
 (0)