Skip to content

Commit c4c8632

Browse files
authored
Merge 49b1699 into 0310da5
2 parents 0310da5 + 49b1699 commit c4c8632

File tree

55 files changed

+1945
-579
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+1945
-579
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
This is only useful as additional information, because the SDK attempts to parse the thread dump into proper threads with stacktraces by default.
2222
- If [ApplicationExitInfo#getTraceInputStream](https://developer.android.com/reference/android/app/ApplicationExitInfo#getTraceInputStream()) returns null, the SDK no longer reports an ANR event, as these events are not very useful without it.
2323
- Enhance regex patterns for native stackframes
24+
- Tracing headers (`sentry-trace` and `baggage`) are now attached and passed through even if performance is disabled ([#2788](https://github.com/getsentry/sentry-java/pull/2788))
2425

2526
## 6.23.0
2627

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

Lines changed: 69 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import io.sentry.protocol.MeasurementValue;
3232
import io.sentry.protocol.TransactionNameSource;
3333
import io.sentry.util.Objects;
34+
import io.sentry.util.TracingUtils;
3435
import java.io.Closeable;
3536
import java.io.IOException;
3637
import java.lang.ref.WeakReference;
@@ -122,11 +123,9 @@ public void register(final @NotNull IHub hub, final @NotNull SentryOptions optio
122123
fullyDisplayedReporter = this.options.getFullyDisplayedReporter();
123124
timeToFullDisplaySpanEnabled = this.options.isEnableTimeToFullDisplayTracing();
124125

125-
if (this.options.isEnableActivityLifecycleBreadcrumbs() || performanceEnabled) {
126-
application.registerActivityLifecycleCallbacks(this);
127-
this.options.getLogger().log(SentryLevel.DEBUG, "ActivityLifecycleIntegration installed.");
128-
addIntegrationToSdkVersion();
129-
}
126+
application.registerActivityLifecycleCallbacks(this);
127+
this.options.getLogger().log(SentryLevel.DEBUG, "ActivityLifecycleIntegration installed.");
128+
addIntegrationToSdkVersion();
130129
}
131130

132131
private boolean isPerformanceEnabled(final @NotNull SentryAndroidOptions options) {
@@ -176,7 +175,9 @@ private void stopPreviousTransactions() {
176175

177176
private void startTracing(final @NotNull Activity activity) {
178177
WeakReference<Activity> weakActivity = new WeakReference<>(activity);
179-
if (performanceEnabled && !isRunningTransaction(activity) && hub != null) {
178+
if (!performanceEnabled && hub != null) {
179+
TracingUtils.startNewTrace(hub);
180+
} else if (performanceEnabled && !isRunningTransaction(activity) && hub != null) {
180181
// as we allow a single transaction running on the bound Scope, we finish the previous ones
181182
stopPreviousTransactions();
182183

@@ -367,44 +368,48 @@ public synchronized void onActivityCreated(
367368

368369
@Override
369370
public synchronized void onActivityStarted(final @NotNull Activity activity) {
370-
// The docs on the screen rendering performance tracing
371-
// (https://firebase.google.com/docs/perf-mon/screen-traces?platform=android#definition),
372-
// state that the tracing starts for every Activity class when the app calls .onActivityStarted.
373-
// Adding an Activity in onActivityCreated leads to Window.FEATURE_NO_TITLE not
374-
// working. Moving this to onActivityStarted fixes the problem.
375-
activityFramesTracker.addActivity(activity);
371+
if (performanceEnabled || options.isEnableActivityLifecycleBreadcrumbs()) {
372+
// The docs on the screen rendering performance tracing
373+
// (https://firebase.google.com/docs/perf-mon/screen-traces?platform=android#definition),
374+
// state that the tracing starts for every Activity class when the app calls
375+
// .onActivityStarted.
376+
// Adding an Activity in onActivityCreated leads to Window.FEATURE_NO_TITLE not
377+
// working. Moving this to onActivityStarted fixes the problem.
378+
activityFramesTracker.addActivity(activity);
379+
}
376380

377381
addBreadcrumb(activity, "started");
378382
}
379383

380384
@SuppressLint("NewApi")
381385
@Override
382386
public synchronized void onActivityResumed(final @NotNull Activity activity) {
383-
384-
// app start span
385-
@Nullable final SentryDate appStartStartTime = AppStartState.getInstance().getAppStartTime();
386-
@Nullable final SentryDate appStartEndTime = AppStartState.getInstance().getAppStartEndTime();
387-
// in case the SentryPerformanceProvider is disabled it does not set the app start times,
388-
// and we need to set the end time manually here,
389-
// the start time gets set manually in SentryAndroid.init()
390-
if (appStartStartTime != null && appStartEndTime == null) {
391-
AppStartState.getInstance().setAppStartEnd();
392-
}
393-
finishAppStartSpan();
394-
395-
final @Nullable ISpan ttidSpan = ttidSpanMap.get(activity);
396-
final @Nullable ISpan ttfdSpan = ttfdSpanMap.get(activity);
397-
final View rootView = activity.findViewById(android.R.id.content);
398-
if (buildInfoProvider.getSdkInfoVersion() >= Build.VERSION_CODES.JELLY_BEAN
399-
&& rootView != null) {
400-
FirstDrawDoneListener.registerForNextDraw(
401-
rootView, () -> onFirstFrameDrawn(ttfdSpan, ttidSpan), buildInfoProvider);
402-
} else {
403-
// Posting a task to the main thread's handler will make it executed after it finished
404-
// its current job. That is, right after the activity draws the layout.
405-
mainHandler.post(() -> onFirstFrameDrawn(ttfdSpan, ttidSpan));
387+
if (performanceEnabled || options.isEnableActivityLifecycleBreadcrumbs()) {
388+
// app start span
389+
@Nullable final SentryDate appStartStartTime = AppStartState.getInstance().getAppStartTime();
390+
@Nullable final SentryDate appStartEndTime = AppStartState.getInstance().getAppStartEndTime();
391+
// in case the SentryPerformanceProvider is disabled it does not set the app start times,
392+
// and we need to set the end time manually here,
393+
// the start time gets set manually in SentryAndroid.init()
394+
if (appStartStartTime != null && appStartEndTime == null) {
395+
AppStartState.getInstance().setAppStartEnd();
396+
}
397+
finishAppStartSpan();
398+
399+
final @Nullable ISpan ttidSpan = ttidSpanMap.get(activity);
400+
final @Nullable ISpan ttfdSpan = ttfdSpanMap.get(activity);
401+
final View rootView = activity.findViewById(android.R.id.content);
402+
if (buildInfoProvider.getSdkInfoVersion() >= Build.VERSION_CODES.JELLY_BEAN
403+
&& rootView != null) {
404+
FirstDrawDoneListener.registerForNextDraw(
405+
rootView, () -> onFirstFrameDrawn(ttfdSpan, ttidSpan), buildInfoProvider);
406+
} else {
407+
// Posting a task to the main thread's handler will make it executed after it finished
408+
// its current job. That is, right after the activity draws the layout.
409+
mainHandler.post(() -> onFirstFrameDrawn(ttfdSpan, ttidSpan));
410+
}
411+
addBreadcrumb(activity, "resumed");
406412
}
407-
addBreadcrumb(activity, "resumed");
408413
}
409414

410415
@Override
@@ -450,35 +455,38 @@ public synchronized void onActivitySaveInstanceState(
450455

451456
@Override
452457
public synchronized void onActivityDestroyed(final @NotNull Activity activity) {
453-
addBreadcrumb(activity, "destroyed");
454-
455-
// in case the appStartSpan isn't completed yet, we finish it as cancelled to avoid
456-
// memory leak
457-
finishSpan(appStartSpan, SpanStatus.CANCELLED);
458+
if (performanceEnabled || options.isEnableActivityLifecycleBreadcrumbs()) {
459+
addBreadcrumb(activity, "destroyed");
458460

459-
// we finish the ttidSpan as cancelled in case it isn't completed yet
460-
final ISpan ttidSpan = ttidSpanMap.get(activity);
461-
final ISpan ttfdSpan = ttfdSpanMap.get(activity);
462-
finishSpan(ttidSpan, SpanStatus.DEADLINE_EXCEEDED);
463-
464-
// we finish the ttfdSpan as deadline_exceeded in case it isn't completed yet
465-
finishExceededTtfdSpan(ttfdSpan, ttidSpan);
466-
cancelTtfdAutoClose();
461+
// in case the appStartSpan isn't completed yet, we finish it as cancelled to avoid
462+
// memory leak
463+
finishSpan(appStartSpan, SpanStatus.CANCELLED);
467464

468-
// in case people opt-out enableActivityLifecycleTracingAutoFinish and forgot to finish it,
469-
// we make sure to finish it when the activity gets destroyed.
470-
stopTracing(activity, true);
465+
// we finish the ttidSpan as cancelled in case it isn't completed yet
466+
final ISpan ttidSpan = ttidSpanMap.get(activity);
467+
final ISpan ttfdSpan = ttfdSpanMap.get(activity);
468+
finishSpan(ttidSpan, SpanStatus.DEADLINE_EXCEEDED);
471469

472-
// set it to null in case its been just finished as cancelled
473-
appStartSpan = null;
474-
ttidSpanMap.remove(activity);
475-
ttfdSpanMap.remove(activity);
470+
// we finish the ttfdSpan as deadline_exceeded in case it isn't completed yet
471+
finishExceededTtfdSpan(ttfdSpan, ttidSpan);
472+
cancelTtfdAutoClose();
476473

477-
// clear it up, so we don't start again for the same activity if the activity is in the activity
478-
// stack still.
479-
// if the activity is opened again and not in memory, transactions will be created normally.
480-
if (performanceEnabled) {
481-
activitiesWithOngoingTransactions.remove(activity);
474+
// in case people opt-out enableActivityLifecycleTracingAutoFinish and forgot to finish it,
475+
// we make sure to finish it when the activity gets destroyed.
476+
stopTracing(activity, true);
477+
478+
// set it to null in case its been just finished as cancelled
479+
appStartSpan = null;
480+
ttidSpanMap.remove(activity);
481+
ttfdSpanMap.remove(activity);
482+
483+
// clear it up, so we don't start again for the same activity if the activity is in the
484+
// activity
485+
// stack still.
486+
// if the activity is opened again and not in memory, transactions will be created normally.
487+
if (performanceEnabled) {
488+
activitiesWithOngoingTransactions.remove(activity);
489+
}
482490
}
483491
}
484492

sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/SentryGestureListener.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import io.sentry.android.core.SentryAndroidOptions;
2121
import io.sentry.internal.gestures.UiElement;
2222
import io.sentry.protocol.TransactionNameSource;
23+
import io.sentry.util.TracingUtils;
2324
import java.lang.ref.WeakReference;
2425
import java.util.Collections;
2526
import java.util.Map;
@@ -186,6 +187,7 @@ private void addBreadcrumb(
186187

187188
private void startTracing(final @NotNull UiElement target, final @NotNull String eventType) {
188189
if (!(options.isTracingEnabled() && options.isEnableUserInteractionTracing())) {
190+
TracingUtils.startNewTrace(hub);
189191
return;
190192
}
191193

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

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import io.sentry.FullyDisplayedReporter
1616
import io.sentry.Hub
1717
import io.sentry.ISentryExecutorService
1818
import io.sentry.Scope
19+
import io.sentry.ScopeCallback
1920
import io.sentry.Sentry
2021
import io.sentry.SentryDate
2122
import io.sentry.SentryLevel
@@ -32,6 +33,7 @@ import io.sentry.protocol.MeasurementValue
3233
import io.sentry.protocol.TransactionNameSource
3334
import io.sentry.test.getProperty
3435
import org.junit.runner.RunWith
36+
import org.mockito.ArgumentCaptor
3537
import org.mockito.kotlin.any
3638
import org.mockito.kotlin.anyOrNull
3739
import org.mockito.kotlin.argumentCaptor
@@ -54,6 +56,7 @@ import kotlin.test.assertEquals
5456
import kotlin.test.assertFalse
5557
import kotlin.test.assertNotEquals
5658
import kotlin.test.assertNotNull
59+
import kotlin.test.assertNotSame
5760
import kotlin.test.assertNull
5861
import kotlin.test.assertSame
5962
import kotlin.test.assertTrue
@@ -141,13 +144,13 @@ class ActivityLifecycleIntegrationTest {
141144
}
142145

143146
@Test
144-
fun `When activity lifecycle breadcrumb and tracing are disabled, it doesn't register callback`() {
147+
fun `When activity lifecycle breadcrumb and tracing are disabled, it still registers callback`() {
145148
val sut = fixture.getSut()
146149
fixture.options.isEnableActivityLifecycleBreadcrumbs = false
147150

148151
sut.register(fixture.hub, fixture.options)
149152

150-
verify(fixture.application, never()).registerActivityLifecycleCallbacks(any())
153+
verify(fixture.application).registerActivityLifecycleCallbacks(any())
151154
}
152155

153156
@Test
@@ -162,15 +165,15 @@ class ActivityLifecycleIntegrationTest {
162165
}
163166

164167
@Test
165-
fun `When activity lifecycle breadcrumb is disabled and tracesSampleRate is set but tracing is disabled, it does not register callback`() {
168+
fun `When activity lifecycle breadcrumb is disabled and tracesSampleRate is set but tracing is disabled, it still registers callback`() {
166169
val sut = fixture.getSut()
167170
fixture.options.isEnableActivityLifecycleBreadcrumbs = false
168171
fixture.options.tracesSampleRate = 1.0
169172
fixture.options.enableTracing = false
170173

171174
sut.register(fixture.hub, fixture.options)
172175

173-
verify(fixture.application, never()).registerActivityLifecycleCallbacks(any())
176+
verify(fixture.application).registerActivityLifecycleCallbacks(any())
174177
}
175178

176179
@Test
@@ -196,7 +199,7 @@ class ActivityLifecycleIntegrationTest {
196199
}
197200

198201
@Test
199-
fun `When activity lifecycle breadcrumb and tracing activity flag are disabled, it doesn't register callback`() {
202+
fun `When activity lifecycle breadcrumb and tracing activity flag are disabled, its still registers callback`() {
200203
val sut = fixture.getSut()
201204
fixture.options.isEnableActivityLifecycleBreadcrumbs = false
202205
fixture.options.tracesSampleRate = 1.0
@@ -205,7 +208,7 @@ class ActivityLifecycleIntegrationTest {
205208

206209
sut.register(fixture.hub, fixture.options)
207210

208-
verify(fixture.application, never()).registerActivityLifecycleCallbacks(any())
211+
verify(fixture.application).registerActivityLifecycleCallbacks(any())
209212
}
210213

211214
@Test
@@ -1384,6 +1387,26 @@ class ActivityLifecycleIntegrationTest {
13841387
assertFalse(ttfdSpan2.isFinished)
13851388
}
13861389

1390+
@Test
1391+
fun `starts new trace if performance is disabled`() {
1392+
val sut = fixture.getSut()
1393+
val activity = mock<Activity>()
1394+
fixture.options.enableTracing = false
1395+
1396+
val argumentCaptor: ArgumentCaptor<ScopeCallback> = ArgumentCaptor.forClass(ScopeCallback::class.java)
1397+
val scope = Scope(fixture.options)
1398+
val propagationContextAtStart = scope.propagationContext
1399+
whenever(fixture.hub.configureScope(argumentCaptor.capture())).thenAnswer {
1400+
argumentCaptor.value.run(scope)
1401+
}
1402+
1403+
sut.register(fixture.hub, fixture.options)
1404+
sut.onActivityCreated(activity, fixture.bundle)
1405+
1406+
verify(fixture.hub).configureScope(any())
1407+
assertNotSame(propagationContextAtStart, scope.propagationContext)
1408+
}
1409+
13871410
private fun runFirstDraw(view: View) {
13881411
// Removes OnDrawListener in the next OnGlobalLayout after onDraw
13891412
view.viewTreeObserver.dispatchOnDraw()

sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerClickTest.kt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,14 @@ import android.widget.CheckBox
1111
import android.widget.RadioButton
1212
import io.sentry.Breadcrumb
1313
import io.sentry.IHub
14+
import io.sentry.Scope
15+
import io.sentry.ScopeCallback
1416
import io.sentry.SentryLevel.INFO
1517
import io.sentry.android.core.SentryAndroidOptions
1618
import org.mockito.kotlin.any
1719
import org.mockito.kotlin.anyOrNull
1820
import org.mockito.kotlin.check
21+
import org.mockito.kotlin.doAnswer
1922
import org.mockito.kotlin.mock
2023
import org.mockito.kotlin.never
2124
import org.mockito.kotlin.verify
@@ -36,6 +39,7 @@ class SentryGestureListenerClickTest {
3639
dsn = "https://[email protected]/proj"
3740
}
3841
val hub = mock<IHub>()
42+
val scope = mock<Scope>()
3943
lateinit var target: View
4044
lateinit var invalidTarget: View
4145

@@ -79,6 +83,7 @@ class SentryGestureListenerClickTest {
7983
whenever(context.resources).thenReturn(resources)
8084
whenever(this.target.context).thenReturn(context)
8185
whenever(activity.window).thenReturn(window)
86+
doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(hub).configureScope(any())
8287
return SentryGestureListener(
8388
activity,
8489
hub,
@@ -228,4 +233,20 @@ class SentryGestureListenerClickTest {
228233
anyOrNull()
229234
)
230235
}
236+
237+
@Test
238+
fun `creates new trace on click`() {
239+
class LocalView(context: Context) : View(context)
240+
241+
val event = mock<MotionEvent>()
242+
val sut = fixture.getSut<LocalView>(event, attachViewsToRoot = false)
243+
fixture.window.mockDecorView<ViewGroup>(event = event, touchWithinBounds = false) {
244+
whenever(it.childCount).thenReturn(1)
245+
whenever(it.getChildAt(0)).thenReturn(fixture.target)
246+
}
247+
248+
sut.onSingleTapUp(event)
249+
250+
verify(fixture.scope).propagationContext = any()
251+
}
231252
}

0 commit comments

Comments
 (0)