Skip to content

Commit 7079193

Browse files
committed
Merge branch 'main' into feat/cpu-info-for-device-context
2 parents 3991f2f + 17ab223 commit 7079193

24 files changed

Lines changed: 798 additions & 49 deletions

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,17 @@
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))
10+
- Add Logcat adapter ([#2620](https://github.com/getsentry/sentry-java/pull/2620))
911
- Provide CPU count/frequency data as device context ([#2622](https://github.com/getsentry/sentry-java/pull/2622))
1012

1113
### Fixes
1214

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

1519
## 6.16.0
1620

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/api/sentry-android-core.api

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,24 @@ public final class io/sentry/android/core/SentryInitProvider {
220220
public fun shutdown ()V
221221
}
222222

223+
public final class io/sentry/android/core/SentryLogcatAdapter {
224+
public fun <init> ()V
225+
public static fun d (Ljava/lang/String;Ljava/lang/String;)I
226+
public static fun d (Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)I
227+
public static fun e (Ljava/lang/String;Ljava/lang/String;)I
228+
public static fun e (Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)I
229+
public static fun i (Ljava/lang/String;Ljava/lang/String;)I
230+
public static fun i (Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)I
231+
public static fun v (Ljava/lang/String;Ljava/lang/String;)I
232+
public static fun v (Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)I
233+
public static fun w (Ljava/lang/String;Ljava/lang/String;)I
234+
public static fun w (Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)I
235+
public static fun w (Ljava/lang/String;Ljava/lang/Throwable;)I
236+
public static fun wtf (Ljava/lang/String;Ljava/lang/String;)I
237+
public static fun wtf (Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)I
238+
public static fun wtf (Ljava/lang/String;Ljava/lang/Throwable;)I
239+
}
240+
223241
public final class io/sentry/android/core/SentryPerformanceProvider : android/app/Application$ActivityLifecycleCallbacks {
224242
public fun <init> ()V
225243
public fun attachInfo (Landroid/content/Context;Landroid/content/pm/ProviderInfo;)V

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,
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
package io.sentry.android.core;
2+
3+
import android.util.Log;
4+
import io.sentry.Breadcrumb;
5+
import io.sentry.Sentry;
6+
import io.sentry.SentryLevel;
7+
import org.jetbrains.annotations.ApiStatus;
8+
import org.jetbrains.annotations.NotNull;
9+
import org.jetbrains.annotations.Nullable;
10+
11+
/**
12+
* This class replaces {@link android.util.Log} with its own implementations which creates a {@link
13+
* io.sentry.Breadcrumb} for each log. It only replaces log functions that meet a minimum level set
14+
* by the user on the Sentry Android Gradle Plugin.
15+
*/
16+
@ApiStatus.Internal
17+
public final class SentryLogcatAdapter {
18+
19+
private static void addAsBreadcrumb(
20+
@Nullable String tag, @NotNull SentryLevel level, @Nullable String msg) {
21+
addAsBreadcrumb(tag, level, msg, null);
22+
}
23+
24+
private static void addAsBreadcrumb(
25+
@Nullable String tag, @NotNull SentryLevel level, @Nullable Throwable tr) {
26+
addAsBreadcrumb(tag, level, null, tr);
27+
}
28+
29+
private static void addAsBreadcrumb(
30+
@Nullable final String tag,
31+
@NotNull final SentryLevel level,
32+
@Nullable final String msg,
33+
@Nullable final Throwable tr) {
34+
Breadcrumb breadcrumb = new Breadcrumb();
35+
breadcrumb.setCategory("Logcat");
36+
breadcrumb.setMessage(msg);
37+
breadcrumb.setLevel(level);
38+
if (tag != null) {
39+
breadcrumb.setData("tag", tag);
40+
}
41+
if (tr != null && tr.getMessage() != null) {
42+
breadcrumb.setData("throwable", tr.getMessage());
43+
}
44+
Sentry.addBreadcrumb(breadcrumb);
45+
}
46+
47+
public static int v(@Nullable String tag, @Nullable String msg) {
48+
addAsBreadcrumb(tag, SentryLevel.DEBUG, msg);
49+
return Log.v(tag, msg);
50+
}
51+
52+
public static int v(@Nullable String tag, @Nullable String msg, @Nullable Throwable tr) {
53+
addAsBreadcrumb(tag, SentryLevel.DEBUG, msg, tr);
54+
return Log.v(tag, msg, tr);
55+
}
56+
57+
public static int d(@Nullable String tag, @Nullable String msg) {
58+
addAsBreadcrumb(tag, SentryLevel.DEBUG, msg);
59+
return Log.d(tag, msg);
60+
}
61+
62+
public static int d(@Nullable String tag, @Nullable String msg, @Nullable Throwable tr) {
63+
addAsBreadcrumb(tag, SentryLevel.DEBUG, msg, tr);
64+
return Log.d(tag, msg, tr);
65+
}
66+
67+
public static int i(@Nullable String tag, @Nullable String msg) {
68+
addAsBreadcrumb(tag, SentryLevel.INFO, msg);
69+
return Log.i(tag, msg);
70+
}
71+
72+
public static int i(@Nullable String tag, @Nullable String msg, @Nullable Throwable tr) {
73+
addAsBreadcrumb(tag, SentryLevel.INFO, msg, tr);
74+
return Log.i(tag, msg, tr);
75+
}
76+
77+
public static int w(@Nullable String tag, @Nullable String msg) {
78+
addAsBreadcrumb(tag, SentryLevel.WARNING, msg);
79+
return Log.w(tag, msg);
80+
}
81+
82+
public static int w(@Nullable String tag, @Nullable String msg, @Nullable Throwable tr) {
83+
addAsBreadcrumb(tag, SentryLevel.WARNING, msg, tr);
84+
return Log.w(tag, msg, tr);
85+
}
86+
87+
public static int w(@Nullable String tag, @Nullable Throwable tr) {
88+
addAsBreadcrumb(tag, SentryLevel.WARNING, tr);
89+
return Log.w(tag, tr);
90+
}
91+
92+
public static int e(@Nullable String tag, @Nullable String msg) {
93+
addAsBreadcrumb(tag, SentryLevel.ERROR, msg);
94+
return Log.e(tag, msg);
95+
}
96+
97+
public static int e(@Nullable String tag, @Nullable String msg, @Nullable Throwable tr) {
98+
addAsBreadcrumb(tag, SentryLevel.ERROR, msg, tr);
99+
return Log.e(tag, msg, tr);
100+
}
101+
102+
public static int wtf(@Nullable String tag, @Nullable String msg) {
103+
addAsBreadcrumb(tag, SentryLevel.ERROR, msg);
104+
return Log.wtf(tag, msg);
105+
}
106+
107+
public static int wtf(@Nullable String tag, @Nullable Throwable tr) {
108+
addAsBreadcrumb(tag, SentryLevel.ERROR, tr);
109+
return Log.wtf(tag, tr);
110+
}
111+
112+
public static int wtf(@Nullable String tag, @Nullable String msg, @Nullable Throwable tr) {
113+
addAsBreadcrumb(tag, SentryLevel.ERROR, msg, tr);
114+
return Log.wtf(tag, msg, tr);
115+
}
116+
}

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()

0 commit comments

Comments
 (0)