Skip to content

Commit

Permalink
Tracing without Performance (#2788)
Browse files Browse the repository at this point in the history
Co-authored-by: Manoel Aranda Neto <5731772+marandaneto@users.noreply.github.com>
Co-authored-by: Roman Zavarnitsyn <rom4ek93@gmail.com>
Co-authored-by: Sentry Github Bot <bot+github-bot@sentry.io>
  • Loading branch information
4 people authored Jun 30, 2023
1 parent dc67004 commit 675fc87
Show file tree
Hide file tree
Showing 55 changed files with 2,177 additions and 669 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### Features

- Add manifest `AutoInit` to integrations list ([#2795](https://github.com/getsentry/sentry-java/pull/2795))
- 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))

### Dependencies

Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import io.sentry.android.core.SentryAndroidOptions;
import io.sentry.internal.gestures.UiElement;
import io.sentry.protocol.TransactionNameSource;
import io.sentry.util.TracingUtils;
import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.Map;
Expand Down Expand Up @@ -185,7 +186,13 @@ private void addBreadcrumb(
}

private void startTracing(final @NotNull UiElement target, final @NotNull String eventType) {
final UiElement uiElement = activeUiElement;
if (!(options.isTracingEnabled() && options.isEnableUserInteractionTracing())) {
if (!(target.equals(uiElement) && eventType.equals(activeEventType))) {
TracingUtils.startNewTrace(hub);
activeUiElement = target;
activeEventType = eventType;
}
return;
}

Expand All @@ -196,7 +203,6 @@ private void startTracing(final @NotNull UiElement target, final @NotNull String
}

final @Nullable String viewIdentifier = target.getIdentifier();
final UiElement uiElement = activeUiElement;

if (activeTransaction != null) {
if (target.equals(uiElement)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import io.sentry.FullyDisplayedReporter
import io.sentry.Hub
import io.sentry.ISentryExecutorService
import io.sentry.Scope
import io.sentry.ScopeCallback
import io.sentry.Sentry
import io.sentry.SentryDate
import io.sentry.SentryLevel
Expand All @@ -32,10 +33,12 @@ import io.sentry.protocol.MeasurementValue
import io.sentry.protocol.TransactionNameSource
import io.sentry.test.getProperty
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.kotlin.any
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.check
import org.mockito.kotlin.clearInvocations
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
Expand All @@ -54,6 +57,7 @@ import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNotEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNotSame
import kotlin.test.assertNull
import kotlin.test.assertSame
import kotlin.test.assertTrue
Expand Down Expand Up @@ -141,13 +145,13 @@ class ActivityLifecycleIntegrationTest {
}

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

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

verify(fixture.application, never()).registerActivityLifecycleCallbacks(any())
verify(fixture.application).registerActivityLifecycleCallbacks(any())
}

@Test
Expand All @@ -162,15 +166,15 @@ class ActivityLifecycleIntegrationTest {
}

@Test
fun `When activity lifecycle breadcrumb is disabled and tracesSampleRate is set but tracing is disabled, it does not register callback`() {
fun `When activity lifecycle breadcrumb is disabled and tracesSampleRate is set but tracing is disabled, it still registers callback`() {
val sut = fixture.getSut()
fixture.options.isEnableActivityLifecycleBreadcrumbs = false
fixture.options.tracesSampleRate = 1.0
fixture.options.enableTracing = false

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

verify(fixture.application, never()).registerActivityLifecycleCallbacks(any())
verify(fixture.application).registerActivityLifecycleCallbacks(any())
}

@Test
Expand All @@ -196,7 +200,7 @@ class ActivityLifecycleIntegrationTest {
}

@Test
fun `When activity lifecycle breadcrumb and tracing activity flag are disabled, it doesn't register callback`() {
fun `When activity lifecycle breadcrumb and tracing activity flag are disabled, its still registers callback`() {
val sut = fixture.getSut()
fixture.options.isEnableActivityLifecycleBreadcrumbs = false
fixture.options.tracesSampleRate = 1.0
Expand All @@ -205,7 +209,7 @@ class ActivityLifecycleIntegrationTest {

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

verify(fixture.application, never()).registerActivityLifecycleCallbacks(any())
verify(fixture.application).registerActivityLifecycleCallbacks(any())
}

@Test
Expand Down Expand Up @@ -1384,6 +1388,60 @@ class ActivityLifecycleIntegrationTest {
assertFalse(ttfdSpan2.isFinished)
}

@Test
fun `starts new trace if performance is disabled`() {
val sut = fixture.getSut()
val activity = mock<Activity>()
fixture.options.enableTracing = false

val argumentCaptor: ArgumentCaptor<ScopeCallback> = ArgumentCaptor.forClass(ScopeCallback::class.java)
val scope = Scope(fixture.options)
val propagationContextAtStart = scope.propagationContext
whenever(fixture.hub.configureScope(argumentCaptor.capture())).thenAnswer {
argumentCaptor.value.run(scope)
}

sut.register(fixture.hub, fixture.options)
sut.onActivityCreated(activity, fixture.bundle)

verify(fixture.hub).configureScope(any())
assertNotSame(propagationContextAtStart, scope.propagationContext)
}

@Test
fun `does not start another new trace if one has already been started but does after activity was destroyed`() {
val sut = fixture.getSut()
val activity = mock<Activity>()
fixture.options.enableTracing = false

val argumentCaptor: ArgumentCaptor<ScopeCallback> = ArgumentCaptor.forClass(ScopeCallback::class.java)
val scope = Scope(fixture.options)
val propagationContextAtStart = scope.propagationContext
whenever(fixture.hub.configureScope(argumentCaptor.capture())).thenAnswer {
argumentCaptor.value.run(scope)
}

sut.register(fixture.hub, fixture.options)
sut.onActivityCreated(activity, fixture.bundle)

verify(fixture.hub).configureScope(any())
val propagationContextAfterNewTrace = scope.propagationContext
assertNotSame(propagationContextAtStart, propagationContextAfterNewTrace)

clearInvocations(fixture.hub)
sut.onActivityCreated(activity, fixture.bundle)

verify(fixture.hub, never()).configureScope(any())
assertSame(propagationContextAfterNewTrace, scope.propagationContext)

sut.onActivityDestroyed(activity)

clearInvocations(fixture.hub)
sut.onActivityCreated(activity, fixture.bundle)
verify(fixture.hub).configureScope(any())
assertNotSame(propagationContextAfterNewTrace, scope.propagationContext)
}

private fun runFirstDraw(view: View) {
// Removes OnDrawListener in the next OnGlobalLayout after onDraw
view.viewTreeObserver.dispatchOnDraw()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,16 @@ import android.widget.CheckBox
import android.widget.RadioButton
import io.sentry.Breadcrumb
import io.sentry.IHub
import io.sentry.PropagationContext
import io.sentry.Scope
import io.sentry.Scope.IWithPropagationContext
import io.sentry.ScopeCallback
import io.sentry.SentryLevel.INFO
import io.sentry.android.core.SentryAndroidOptions
import org.mockito.kotlin.any
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.check
import org.mockito.kotlin.doAnswer
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.verify
Expand All @@ -36,6 +41,8 @@ class SentryGestureListenerClickTest {
dsn = "https://key@sentry.io/proj"
}
val hub = mock<IHub>()
val scope = mock<Scope>()
val propagationContext = PropagationContext()
lateinit var target: View
lateinit var invalidTarget: View

Expand Down Expand Up @@ -79,6 +86,8 @@ class SentryGestureListenerClickTest {
whenever(context.resources).thenReturn(resources)
whenever(this.target.context).thenReturn(context)
whenever(activity.window).thenReturn(window)
doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(hub).configureScope(any())
doAnswer { (it.arguments[0] as IWithPropagationContext).accept(propagationContext); propagationContext; }.whenever(scope).withPropagationContext(any())
return SentryGestureListener(
activity,
hub,
Expand Down Expand Up @@ -228,4 +237,20 @@ class SentryGestureListenerClickTest {
anyOrNull()
)
}

@Test
fun `creates new trace on click`() {
class LocalView(context: Context) : View(context)

val event = mock<MotionEvent>()
val sut = fixture.getSut<LocalView>(event, attachViewsToRoot = false)
fixture.window.mockDecorView<ViewGroup>(event = event, touchWithinBounds = false) {
whenever(it.childCount).thenReturn(1)
whenever(it.getChildAt(0)).thenReturn(fixture.target)
}

sut.onSingleTapUp(event)

verify(fixture.scope).propagationContext = any()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,20 @@ import android.widget.ListAdapter
import androidx.core.view.ScrollingView
import io.sentry.Breadcrumb
import io.sentry.IHub
import io.sentry.PropagationContext
import io.sentry.Scope
import io.sentry.ScopeCallback
import io.sentry.SentryLevel.INFO
import io.sentry.android.core.SentryAndroidOptions
import org.mockito.kotlin.any
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.check
import org.mockito.kotlin.clearInvocations
import org.mockito.kotlin.doAnswer
import org.mockito.kotlin.inOrder
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import org.mockito.kotlin.verifyNoMoreInteractions
import org.mockito.kotlin.whenever
Expand All @@ -39,6 +45,8 @@ class SentryGestureListenerScrollTest {
gestureTargetLocators = listOf(AndroidViewGestureTargetLocator(true))
}
val hub = mock<IHub>()
val scope = mock<Scope>()
val propagationContext = PropagationContext()

val firstEvent = mock<MotionEvent>()
val eventsInBetween = listOf(mock<MotionEvent>(), mock(), mock())
Expand Down Expand Up @@ -69,6 +77,8 @@ class SentryGestureListenerScrollTest {
endEvent.mockDirection(firstEvent, direction)
}
whenever(activity.window).thenReturn(window)
doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(hub).configureScope(any())
doAnswer { (it.arguments[0] as Scope.IWithPropagationContext).accept(propagationContext); propagationContext }.whenever(scope).withPropagationContext(any())
return SentryGestureListener(
activity,
hub,
Expand Down Expand Up @@ -145,6 +155,7 @@ class SentryGestureListenerScrollTest {
},
anyOrNull()
)
verify(fixture.hub).configureScope(anyOrNull())
verify(fixture.hub).addBreadcrumb(
check<Breadcrumb> {
assertEquals("ui.swipe", it.category)
Expand Down Expand Up @@ -182,6 +193,50 @@ class SentryGestureListenerScrollTest {
verify(fixture.hub, never()).addBreadcrumb(any<Breadcrumb>())
}

@Test
fun `starts a new trace on scroll`() {
val sut = fixture.getSut<ScrollableListView>(direction = "left")

sut.onDown(fixture.firstEvent)
fixture.eventsInBetween.forEach {
sut.onScroll(fixture.firstEvent, it, 10.0f, 0f)
}
sut.onUp(fixture.endEvent)

verify(fixture.scope).propagationContext = any()
}

@Test
fun `does not start a new trace on repeated scroll but does for a different event`() {
val sut = fixture.getSut<ScrollableListView>(direction = "left")

sut.onDown(fixture.firstEvent)
fixture.eventsInBetween.forEach {
sut.onScroll(fixture.firstEvent, it, 10.0f, 0f)
}
sut.onUp(fixture.endEvent)

verify(fixture.scope).propagationContext = any()

clearInvocations(fixture.scope)

sut.onDown(fixture.firstEvent)
fixture.eventsInBetween.forEach {
sut.onScroll(fixture.firstEvent, it, 10.0f, 0f)
}
sut.onUp(fixture.endEvent)
verify(fixture.scope, never()).propagationContext = any()

clearInvocations(fixture.scope)

sut.onDown(fixture.firstEvent)
fixture.eventsInBetween.forEach { sut.onScroll(fixture.firstEvent, it, 0f, 30.0f) }
sut.onFling(fixture.firstEvent, fixture.endEvent, 1.0f, 1.0f)
sut.onUp(fixture.endEvent)

verify(fixture.scope).propagationContext = any()
}

internal class ScrollableView : View(mock()), ScrollingView {
override fun computeVerticalScrollOffset(): Int = 0
override fun computeVerticalScrollExtent(): Int = 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import android.widget.AbsListView
import android.widget.ListAdapter
import io.sentry.IHub
import io.sentry.Scope
import io.sentry.ScopeCallback
import io.sentry.SentryTracer
import io.sentry.SpanStatus
import io.sentry.TransactionContext
Expand All @@ -20,6 +21,7 @@ import io.sentry.protocol.TransactionNameSource
import org.mockito.kotlin.any
import org.mockito.kotlin.check
import org.mockito.kotlin.clearInvocations
import org.mockito.kotlin.doAnswer
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.verify
Expand All @@ -40,6 +42,7 @@ class SentryGestureListenerTracingTest {
}
val hub = mock<IHub>()
val event = mock<MotionEvent>()
val scope = mock<Scope>()
lateinit var target: View
lateinit var transaction: SentryTracer

Expand Down Expand Up @@ -79,6 +82,7 @@ class SentryGestureListenerTracingTest {

whenever(hub.startTransaction(any(), any<TransactionOptions>()))
.thenReturn(this.transaction)
doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(hub).configureScope(any())

return SentryGestureListener(
activity,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import io.sentry.TransactionContext
import io.sentry.TransactionOptions
import io.sentry.TypeCheckHint
import io.sentry.protocol.TransactionNameSource
import io.sentry.util.TracingUtils
import java.lang.ref.WeakReference

/**
Expand Down Expand Up @@ -96,6 +97,7 @@ class SentryNavigationListener @JvmOverloads constructor(
arguments: Map<String, Any?>
) {
if (!isPerformanceEnabled) {
TracingUtils.startNewTrace(hub)
return
}

Expand Down
Loading

0 comments on commit 675fc87

Please sign in to comment.