Skip to content

Commit e22ce45

Browse files
authored
Instrumentation API part 7 (open-telemetry#465)
* Removing fragment lifecycle callbacks from fragment instrumentation * Adding FragmentLifecycleInstrumentation * Removing lifecycle module * Updating tests * Reusing lifecycle instrumentation scope
1 parent be5d084 commit e22ce45

File tree

10 files changed

+137
-246
lines changed

10 files changed

+137
-246
lines changed

android-agent/src/main/java/io/opentelemetry/android/internal/services/visiblescreen/fragments/VisibleFragmentTracker.kt

-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ class VisibleFragmentTracker(private val visibleScreenService: VisibleScreenServ
2222
fm: FragmentManager,
2323
f: Fragment,
2424
) {
25-
super.onFragmentPaused(fm, f)
2625
visibleScreenService.fragmentPaused(f)
2726
}
2827
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.android.internal.services.visiblescreen.fragments
7+
8+
import androidx.fragment.app.Fragment
9+
import io.mockk.Runs
10+
import io.mockk.every
11+
import io.mockk.just
12+
import io.mockk.mockk
13+
import io.mockk.verify
14+
import io.opentelemetry.android.internal.services.visiblescreen.VisibleScreenService
15+
import org.junit.jupiter.api.BeforeEach
16+
import org.junit.jupiter.api.Test
17+
18+
class VisibleFragmentTrackerTest {
19+
private lateinit var service: VisibleScreenService
20+
private lateinit var fragment: Fragment
21+
private lateinit var visibleFragmentTracker: VisibleFragmentTracker
22+
23+
@BeforeEach
24+
fun setUp() {
25+
service = mockk()
26+
fragment = mockk()
27+
visibleFragmentTracker = VisibleFragmentTracker(service)
28+
}
29+
30+
@Test
31+
fun `Track fragment resumed`() {
32+
every { service.fragmentResumed(any()) } just Runs
33+
34+
visibleFragmentTracker.onFragmentResumed(mockk(), fragment)
35+
36+
verify {
37+
service.fragmentResumed(fragment)
38+
}
39+
}
40+
41+
@Test
42+
fun `Track fragment paused`() {
43+
every { service.fragmentPaused(any()) } just Runs
44+
45+
visibleFragmentTracker.onFragmentPaused(mockk(), fragment)
46+
47+
verify {
48+
service.fragmentPaused(fragment)
49+
}
50+
}
51+
}

instrumentation/activity/src/main/java/io/opentelemetry/android/instrumentation/activity/ActivityLifecycleInstrumentation.kt

+1-4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import android.os.Build
1010
import io.opentelemetry.android.OpenTelemetryRum
1111
import io.opentelemetry.android.instrumentation.AndroidInstrumentation
1212
import io.opentelemetry.android.instrumentation.activity.startup.AppStartupTimer
13+
import io.opentelemetry.android.instrumentation.common.Constants.INSTRUMENTATION_SCOPE
1314
import io.opentelemetry.android.instrumentation.common.ScreenNameExtractor
1415
import io.opentelemetry.android.internal.services.ServiceManager
1516
import io.opentelemetry.android.internal.services.visiblescreen.activities.DefaultingActivityLifecycleCallbacks
@@ -20,10 +21,6 @@ class ActivityLifecycleInstrumentation : AndroidInstrumentation {
2021
private var screenNameExtractor: ScreenNameExtractor = ScreenNameExtractor.DEFAULT
2122
private var tracerCustomizer: (Tracer) -> Tracer = { it }
2223

23-
companion object {
24-
private const val INSTRUMENTATION_SCOPE: String = "io.opentelemetry.lifecycle"
25-
}
26-
2724
fun setTracerCustomizer(customizer: (Tracer) -> Tracer) {
2825
tracerCustomizer = customizer
2926
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.android.instrumentation.common
7+
8+
object Constants {
9+
const val INSTRUMENTATION_SCOPE = "io.opentelemetry.lifecycle"
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.android.instrumentation.fragment
7+
8+
import android.app.Application
9+
import android.app.Application.ActivityLifecycleCallbacks
10+
import android.os.Build
11+
import io.opentelemetry.android.OpenTelemetryRum
12+
import io.opentelemetry.android.instrumentation.AndroidInstrumentation
13+
import io.opentelemetry.android.instrumentation.common.Constants.INSTRUMENTATION_SCOPE
14+
import io.opentelemetry.android.instrumentation.common.ScreenNameExtractor
15+
import io.opentelemetry.android.internal.services.ServiceManager
16+
import io.opentelemetry.android.internal.services.visiblescreen.fragments.RumFragmentActivityRegisterer
17+
import io.opentelemetry.api.trace.Tracer
18+
19+
class FragmentLifecycleInstrumentation : AndroidInstrumentation {
20+
private var screenNameExtractor = ScreenNameExtractor.DEFAULT
21+
private var tracerCustomizer: (Tracer) -> Tracer = { it }
22+
23+
fun setTracerCustomizer(customizer: (Tracer) -> Tracer) {
24+
tracerCustomizer = customizer
25+
}
26+
27+
fun setScreenNameExtractor(screenNameExtractor: ScreenNameExtractor) {
28+
this.screenNameExtractor = screenNameExtractor
29+
}
30+
31+
override fun install(
32+
application: Application,
33+
openTelemetryRum: OpenTelemetryRum,
34+
) {
35+
application.registerActivityLifecycleCallbacks(buildFragmentRegisterer(openTelemetryRum))
36+
}
37+
38+
private fun buildFragmentRegisterer(openTelemetryRum: OpenTelemetryRum): ActivityLifecycleCallbacks {
39+
val visibleScreenService = ServiceManager.get().getVisibleScreenService()
40+
val delegateTracer: Tracer = openTelemetryRum.openTelemetry.getTracer(INSTRUMENTATION_SCOPE)
41+
val fragmentLifecycle =
42+
RumFragmentLifecycleCallbacks(
43+
tracerCustomizer.invoke(delegateTracer),
44+
visibleScreenService::previouslyVisibleScreen,
45+
screenNameExtractor,
46+
)
47+
return if (Build.VERSION.SDK_INT < 29) {
48+
RumFragmentActivityRegisterer.createPre29(fragmentLifecycle)
49+
} else {
50+
RumFragmentActivityRegisterer.create(fragmentLifecycle)
51+
}
52+
}
53+
}

instrumentation/fragment/src/main/java/io/opentelemetry/android/instrumentation/fragment/RumFragmentLifecycleCallbacks.java

+5-9
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,24 @@
1414
import androidx.fragment.app.FragmentManager;
1515
import io.opentelemetry.android.common.ActiveSpan;
1616
import io.opentelemetry.android.instrumentation.common.ScreenNameExtractor;
17-
import io.opentelemetry.android.internal.services.visiblescreen.VisibleScreenService;
1817
import io.opentelemetry.api.trace.Tracer;
1918
import java.util.HashMap;
2019
import java.util.Map;
20+
import java.util.function.Supplier;
2121

2222
public class RumFragmentLifecycleCallbacks extends FragmentManager.FragmentLifecycleCallbacks {
2323
private final Map<String, FragmentTracer> tracersByFragmentClassName = new HashMap<>();
2424

2525
private final Tracer tracer;
26-
private final VisibleScreenService visibleScreenService;
26+
private final Supplier<String> lastVisibleScreen;
2727
private final ScreenNameExtractor screenNameExtractor;
2828

2929
public RumFragmentLifecycleCallbacks(
3030
Tracer tracer,
31-
VisibleScreenService visibleScreenService,
31+
Supplier<String> lastVisibleScreen,
3232
ScreenNameExtractor screenNameExtractor) {
3333
this.tracer = tracer;
34-
this.visibleScreenService = visibleScreenService;
34+
this.lastVisibleScreen = lastVisibleScreen;
3535
this.screenNameExtractor = screenNameExtractor;
3636
}
3737

@@ -87,13 +87,11 @@ public void onFragmentResumed(@NonNull FragmentManager fm, @NonNull Fragment f)
8787
.addEvent("fragmentResumed")
8888
.addPreviousScreenAttribute()
8989
.endActiveSpan();
90-
visibleScreenService.fragmentResumed(f);
9190
}
9291

9392
@Override
9493
public void onFragmentPaused(@NonNull FragmentManager fm, @NonNull Fragment f) {
9594
super.onFragmentPaused(fm, f);
96-
visibleScreenService.fragmentPaused(f);
9795
getTracer(f).startSpanIfNoneInProgress("Paused").addEvent("fragmentPaused");
9896
}
9997

@@ -152,9 +150,7 @@ private FragmentTracer getTracer(Fragment fragment) {
152150
FragmentTracer.builder(fragment)
153151
.setTracer(tracer)
154152
.setScreenName(screenNameExtractor.extract(fragment))
155-
.setActiveSpan(
156-
new ActiveSpan(
157-
visibleScreenService::getPreviouslyVisibleScreen))
153+
.setActiveSpan(new ActiveSpan(lastVisibleScreen))
158154
.build();
159155
tracersByFragmentClassName.put(fragment.getClass().getName(), activityTracer);
160156
}

instrumentation/fragment/src/test/java/io/opentelemetry/android/instrumentation/fragment/RumFragmentLifecycleCallbacksTest.java

+17-32
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import static org.mockito.Mockito.mock;
1616
import static org.mockito.Mockito.when;
1717

18+
import androidx.annotation.NonNull;
1819
import androidx.fragment.app.Fragment;
1920
import io.opentelemetry.android.instrumentation.common.ScreenNameExtractor;
2021
import io.opentelemetry.android.internal.services.visiblescreen.VisibleScreenService;
@@ -48,10 +49,7 @@ void setup() {
4849

4950
@Test
5051
void fragmentCreation() {
51-
FragmentCallbackTestHarness testHarness =
52-
new FragmentCallbackTestHarness(
53-
new RumFragmentLifecycleCallbacks(
54-
tracer, visibleScreenService, screenNameExtractor));
52+
FragmentCallbackTestHarness testHarness = getFragmentCallbackTestHarness();
5553

5654
Fragment fragment = mock(Fragment.class);
5755
testHarness.runFragmentCreationLifecycle(fragment);
@@ -83,10 +81,7 @@ void fragmentCreation() {
8381
@Test
8482
void fragmentRestored() {
8583
when(visibleScreenService.getPreviouslyVisibleScreen()).thenReturn("previousScreen");
86-
FragmentCallbackTestHarness testHarness =
87-
new FragmentCallbackTestHarness(
88-
new RumFragmentLifecycleCallbacks(
89-
tracer, visibleScreenService, screenNameExtractor));
84+
FragmentCallbackTestHarness testHarness = getFragmentCallbackTestHarness();
9085

9186
Fragment fragment = mock(Fragment.class);
9287
testHarness.runFragmentRestoredLifecycle(fragment);
@@ -113,10 +108,7 @@ void fragmentRestored() {
113108

114109
@Test
115110
void fragmentResumed() {
116-
FragmentCallbackTestHarness testHarness =
117-
new FragmentCallbackTestHarness(
118-
new RumFragmentLifecycleCallbacks(
119-
tracer, visibleScreenService, screenNameExtractor));
111+
FragmentCallbackTestHarness testHarness = getFragmentCallbackTestHarness();
120112

121113
Fragment fragment = mock(Fragment.class);
122114
testHarness.runFragmentResumedLifecycle(fragment);
@@ -139,10 +131,7 @@ void fragmentResumed() {
139131

140132
@Test
141133
void fragmentPaused() {
142-
FragmentCallbackTestHarness testHarness =
143-
new FragmentCallbackTestHarness(
144-
new RumFragmentLifecycleCallbacks(
145-
tracer, visibleScreenService, screenNameExtractor));
134+
FragmentCallbackTestHarness testHarness = getFragmentCallbackTestHarness();
146135

147136
Fragment fragment = mock(Fragment.class);
148137
testHarness.runFragmentPausedLifecycle(fragment);
@@ -168,10 +157,7 @@ void fragmentPaused() {
168157

169158
@Test
170159
void fragmentDetachedFromActive() {
171-
FragmentCallbackTestHarness testHarness =
172-
new FragmentCallbackTestHarness(
173-
new RumFragmentLifecycleCallbacks(
174-
tracer, visibleScreenService, screenNameExtractor));
160+
FragmentCallbackTestHarness testHarness = getFragmentCallbackTestHarness();
175161

176162
Fragment fragment = mock(Fragment.class);
177163
testHarness.runFragmentDetachedFromActiveLifecycle(fragment);
@@ -224,10 +210,7 @@ void fragmentDetachedFromActive() {
224210

225211
@Test
226212
void fragmentDestroyedFromStopped() {
227-
FragmentCallbackTestHarness testHarness =
228-
new FragmentCallbackTestHarness(
229-
new RumFragmentLifecycleCallbacks(
230-
tracer, visibleScreenService, screenNameExtractor));
213+
FragmentCallbackTestHarness testHarness = getFragmentCallbackTestHarness();
231214

232215
Fragment fragment = mock(Fragment.class);
233216
testHarness.runFragmentViewDestroyedFromStoppedLifecycle(fragment);
@@ -252,10 +235,7 @@ void fragmentDestroyedFromStopped() {
252235

253236
@Test
254237
void fragmentDetachedFromStopped() {
255-
FragmentCallbackTestHarness testHarness =
256-
new FragmentCallbackTestHarness(
257-
new RumFragmentLifecycleCallbacks(
258-
tracer, visibleScreenService, screenNameExtractor));
238+
FragmentCallbackTestHarness testHarness = getFragmentCallbackTestHarness();
259239

260240
Fragment fragment = mock(Fragment.class);
261241
testHarness.runFragmentDetachedFromStoppedLifecycle(fragment);
@@ -294,10 +274,7 @@ void fragmentDetachedFromStopped() {
294274

295275
@Test
296276
void fragmentDetached() {
297-
FragmentCallbackTestHarness testHarness =
298-
new FragmentCallbackTestHarness(
299-
new RumFragmentLifecycleCallbacks(
300-
tracer, visibleScreenService, screenNameExtractor));
277+
FragmentCallbackTestHarness testHarness = getFragmentCallbackTestHarness();
301278

302279
Fragment fragment = mock(Fragment.class);
303280
testHarness.runFragmentDetachedLifecycle(fragment);
@@ -326,4 +303,12 @@ private void checkEventExists(List<EventData> events, String eventName) {
326303
events.stream().filter(e -> e.getName().equals(eventName)).findAny();
327304
assertTrue(event.isPresent(), "Event with name " + eventName + " not found");
328305
}
306+
307+
private @NonNull FragmentCallbackTestHarness getFragmentCallbackTestHarness() {
308+
return new FragmentCallbackTestHarness(
309+
new RumFragmentLifecycleCallbacks(
310+
tracer,
311+
visibleScreenService::getPreviouslyVisibleScreen,
312+
screenNameExtractor));
313+
}
329314
}

instrumentation/lifecycle/build.gradle.kts

-27
This file was deleted.

0 commit comments

Comments
 (0)