Skip to content

Commit 053e0ec

Browse files
committed
Enable manual creation of screen view spans
1 parent 1bc3c4c commit 053e0ec

File tree

9 files changed

+149
-8
lines changed

9 files changed

+149
-8
lines changed

sample-app/src/main/java/com/splunk/android/sample/FirstFragment.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
8080
NavHostFragment.findNavController(FirstFragment.this)
8181
.navigate(R.id.action_FirstFragment_to_SecondFragment));
8282

83+
binding.buttonScreenName.setOnClickListener(v -> {
84+
SplunkRum.getInstance().setScreenName("custom-screen");
85+
});
86+
8387
binding.crash.setOnClickListener(v -> multiThreadCrashing());
8488

8589
binding.httpMe.setOnClickListener(
@@ -184,12 +188,22 @@ public LiveData<String> getSessionId() {
184188
public void onDestroyView() {
185189
super.onDestroyView();
186190
binding = null;
191+
192+
SplunkRum.getInstance().setScreenName(null);
187193
}
188194

189195
@Override
190196
public void onResume() {
191197
super.onResume();
192198
sessionId.postValue(splunkRum.getRumSessionId());
199+
200+
SplunkRum.getInstance().setScreenName("custom-resumed-name", "Resumed");
201+
}
202+
203+
@Override
204+
public void onPause() {
205+
super.onPause();
206+
SplunkRum.getInstance().setScreenName(null);
193207
}
194208

195209
@SuppressLint("AllowAllHostnameVerifier")

sample-app/src/main/res/layout/fragment_first.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@
4242
android:layout_height="wrap_content"
4343
android:text="@string/next" />
4444

45+
<Button
46+
android:id="@+id/button_screen_name"
47+
android:layout_width="wrap_content"
48+
android:layout_height="wrap_content"
49+
android:text="@string/screen_name" />
50+
4551
<Button
4652
android:id="@+id/crash"
4753
android:layout_width="wrap_content"

sample-app/src/main/res/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
<string name="second_fragment_label">Second Fragment</string>
77
<string name="next">Next</string>
88
<string name="previous">Previous</string>
9+
<string name="screen_name">Custom screen name</string>
910

1011
<string name="hello_first_fragment">Hello first fragment</string>
1112
<string name="hello_second_fragment">Hello second fragment. Arg: %1$s</string>
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.splunk.rum;
2+
3+
import androidx.annotation.Nullable;
4+
5+
import java.util.concurrent.atomic.AtomicReference;
6+
7+
import io.opentelemetry.android.instrumentation.activity.VisibleScreenTracker;
8+
9+
public class ExplicitVisibleScreenNameTracker extends VisibleScreenTracker {
10+
private final AtomicReference<String> lastScreenName = new AtomicReference<>();
11+
private final AtomicReference<String> previouslyLastScreenName = new AtomicReference<>();
12+
13+
public void setExplicitScreenName(String screenName) {
14+
this.previouslyLastScreenName.set(this.lastScreenName.get());
15+
this.lastScreenName.set(screenName);
16+
}
17+
18+
@Nullable
19+
@Override
20+
public String getPreviouslyVisibleScreen() {
21+
String screenName = previouslyLastScreenName.get();
22+
if (screenName != null) {
23+
return screenName;
24+
}
25+
26+
return super.getPreviouslyVisibleScreen();
27+
}
28+
29+
@Override
30+
public String getCurrentlyVisibleScreen() {
31+
String screenName = lastScreenName.get();
32+
if (screenName != null) {
33+
return screenName;
34+
}
35+
36+
return super.getCurrentlyVisibleScreen();
37+
}
38+
39+
}

splunk-otel-android/src/main/java/com/splunk/rum/NoOpSplunkRum.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class NoOpSplunkRum extends SplunkRum {
3232
// passing null values here is fine, they'll never get used anyway
3333
@SuppressWarnings("NullAway")
3434
private NoOpSplunkRum() {
35-
super(OpenTelemetryRum.noop(), null);
35+
super(OpenTelemetryRum.noop(), null, null);
3636
}
3737

3838
@Override
@@ -79,4 +79,9 @@ public void integrateWithBrowserRum(WebView webView) {
7979
void flushSpans() {
8080
// no-op
8181
}
82+
83+
@Override
84+
public void setScreenName(String screenName) {
85+
// no-op
86+
}
8287
}

splunk-otel-android/src/main/java/com/splunk/rum/RumInitializer.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import io.opentelemetry.android.OpenTelemetryRum;
3737
import io.opentelemetry.android.OpenTelemetryRumBuilder;
3838
import io.opentelemetry.android.RuntimeDetailsExtractor;
39+
import io.opentelemetry.android.ScreenAttributesSpanProcessor;
3940
import io.opentelemetry.android.config.OtelRumConfig;
4041
import io.opentelemetry.android.instrumentation.activity.VisibleScreenTracker;
4142
import io.opentelemetry.android.instrumentation.anr.AnrDetector;
@@ -52,6 +53,7 @@
5253
import io.opentelemetry.sdk.resources.Resource;
5354
import io.opentelemetry.sdk.resources.ResourceBuilder;
5455
import io.opentelemetry.sdk.trace.SpanLimits;
56+
import io.opentelemetry.sdk.trace.SpanProcessor;
5557
import io.opentelemetry.sdk.trace.data.SpanData;
5658
import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;
5759
import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor;
@@ -86,7 +88,7 @@ class RumInitializer {
8688
}
8789

8890
SplunkRum initialize(Looper mainLooper) {
89-
VisibleScreenTracker visibleScreenTracker = new VisibleScreenTracker();
91+
ExplicitVisibleScreenNameTracker visibleScreenTracker = new ExplicitVisibleScreenNameTracker();
9092

9193
initializationEvents.begin();
9294

@@ -98,6 +100,7 @@ SplunkRum initialize(Looper mainLooper) {
98100
config.disableNetworkChangeMonitoring();
99101
}
100102

103+
config.disableScreenAttributes();
101104
OpenTelemetryRumBuilder otelRumBuilder = OpenTelemetryRum.builder(application, config);
102105

103106
otelRumBuilder.mergeResource(createSplunkResource());
@@ -193,6 +196,13 @@ SplunkRum initialize(Looper mainLooper) {
193196
installCrashReporter(otelRumBuilder);
194197
}
195198

199+
otelRumBuilder.addTracerProviderCustomizer(
200+
(tracerProviderBuilder, app) -> {
201+
SpanProcessor screenAttributesAppender =
202+
new ScreenAttributesSpanProcessor(visibleScreenTracker);
203+
return tracerProviderBuilder.addSpanProcessor(screenAttributesAppender);
204+
});
205+
196206
// Lifecycle events instrumentation are always installed.
197207
installLifecycleInstrumentations(otelRumBuilder, visibleScreenTracker);
198208

@@ -204,7 +214,7 @@ SplunkRum initialize(Looper mainLooper) {
204214
builder.getConfigFlags(),
205215
openTelemetryRum.getOpenTelemetry().getTracer(RUM_TRACER_NAME));
206216

207-
return new SplunkRum(openTelemetryRum, globalAttributeSupplier);
217+
return new SplunkRum(openTelemetryRum, globalAttributeSupplier, visibleScreenTracker);
208218
}
209219

210220
@NonNull

splunk-otel-android/src/main/java/com/splunk/rum/SplunkRum.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package com.splunk.rum;
1818

19+
import static io.opentelemetry.android.RumConstants.LAST_SCREEN_NAME_KEY;
20+
import static io.opentelemetry.android.RumConstants.SCREEN_NAME_KEY;
1921
import static io.opentelemetry.api.common.AttributeKey.doubleKey;
2022
import static io.opentelemetry.api.common.AttributeKey.stringKey;
2123

@@ -28,6 +30,7 @@
2830
import androidx.annotation.Nullable;
2931
import com.splunk.rum.internal.GlobalAttributesSupplier;
3032
import io.opentelemetry.android.OpenTelemetryRum;
33+
import io.opentelemetry.android.instrumentation.activity.VisibleScreenTracker;
3134
import io.opentelemetry.android.instrumentation.startup.AppStartupTimer;
3235
import io.opentelemetry.api.OpenTelemetry;
3336
import io.opentelemetry.api.common.AttributeKey;
@@ -72,15 +75,17 @@ public class SplunkRum {
7275

7376
private final OpenTelemetryRum openTelemetryRum;
7477
private final GlobalAttributesSupplier globalAttributes;
78+
private final ExplicitVisibleScreenNameTracker visibleScreenTracker;
7579

7680
static {
7781
Handler handler = new Handler(Looper.getMainLooper());
7882
startupTimer.detectBackgroundStart(handler);
7983
}
8084

81-
SplunkRum(OpenTelemetryRum openTelemetryRum, GlobalAttributesSupplier globalAttributes) {
85+
SplunkRum(OpenTelemetryRum openTelemetryRum, GlobalAttributesSupplier globalAttributes, ExplicitVisibleScreenNameTracker visibleScreenTracker) {
8286
this.openTelemetryRum = openTelemetryRum;
8387
this.globalAttributes = globalAttributes;
88+
this.visibleScreenTracker = visibleScreenTracker;
8489
}
8590

8691
/** Creates a new {@link SplunkRumBuilder}, used to set up a {@link SplunkRum} instance. */
@@ -113,6 +118,24 @@ static SplunkRum initialize(SplunkRumBuilder builder, Application application) {
113118
return INSTANCE;
114119
}
115120

121+
public void setScreenName(String screenName, String spanType) {
122+
visibleScreenTracker.setExplicitScreenName(screenName);
123+
124+
if (screenName != null) {
125+
Span span = getTracer()
126+
.spanBuilder(spanType)
127+
.setAttribute(COMPONENT_KEY, "ui")
128+
.startSpan();
129+
span.setAttribute(SCREEN_NAME_KEY, screenName);
130+
span.setAttribute(LAST_SCREEN_NAME_KEY, visibleScreenTracker.getPreviouslyVisibleScreen());
131+
span.end();
132+
}
133+
}
134+
135+
public void setScreenName(String screenName) {
136+
setScreenName(screenName, "Created");
137+
}
138+
116139
/** Returns {@code true} if the Splunk RUM library has been successfully initialized. */
117140
public static boolean isInitialized() {
118141
return INSTANCE != null;

splunk-otel-android/src/test/java/com/splunk/rum/NoOpSplunkRumTest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,5 +50,8 @@ void doesNotThrow() {
5050

5151
Location location = mock(Location.class);
5252
instance.updateLocation(location);
53+
54+
instance.setScreenName("explicit-screen-name");
55+
instance.flushSpans();
5356
}
5457
}

splunk-otel-android/src/test/java/com/splunk/rum/SplunkRumTest.java

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,14 @@
1717
package com.splunk.rum;
1818

1919
import static com.splunk.rum.SplunkRum.COMPONENT_KEY;
20+
import static io.opentelemetry.android.RumConstants.LAST_SCREEN_NAME_KEY;
21+
import static io.opentelemetry.android.RumConstants.SCREEN_NAME_KEY;
2022
import static io.opentelemetry.api.common.AttributeKey.longKey;
2123
import static io.opentelemetry.api.common.AttributeKey.stringKey;
2224
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
2325
import static org.junit.jupiter.api.Assertions.assertEquals;
2426
import static org.junit.jupiter.api.Assertions.assertNotNull;
27+
import static org.junit.jupiter.api.Assertions.assertNull;
2528
import static org.junit.jupiter.api.Assertions.assertSame;
2629
import static org.junit.jupiter.api.Assertions.assertTrue;
2730
import static org.mockito.ArgumentMatchers.eq;
@@ -70,8 +73,12 @@ public class SplunkRumTest {
7073
@Mock private OpenTelemetryRum openTelemetryRum;
7174
@Mock private GlobalAttributesSupplier globalAttributes;
7275

76+
private ExplicitVisibleScreenNameTracker screenNameTracker;
77+
7378
@BeforeEach
7479
public void setup() {
80+
screenNameTracker = new ExplicitVisibleScreenNameTracker();
81+
7582
tracer = otelTesting.getOpenTelemetry().getTracer("testTracer");
7683
SplunkRum.resetSingletonForTest();
7784
}
@@ -148,7 +155,7 @@ void nonNullMethods() {
148155
void addEvent() {
149156
when(openTelemetryRum.getOpenTelemetry()).thenReturn(otelTesting.getOpenTelemetry());
150157

151-
SplunkRum splunkRum = new SplunkRum(openTelemetryRum, globalAttributes);
158+
SplunkRum splunkRum = new SplunkRum(openTelemetryRum, globalAttributes, screenNameTracker);
152159

153160
Attributes attributes = Attributes.of(stringKey("one"), "1", longKey("two"), 2L);
154161
splunkRum.addRumEvent("foo", attributes);
@@ -166,7 +173,7 @@ void addException() {
166173

167174
when(openTelemetryRum.getOpenTelemetry()).thenReturn(testSdk);
168175

169-
SplunkRum splunkRum = new SplunkRum(openTelemetryRum, globalAttributes);
176+
SplunkRum splunkRum = new SplunkRum(openTelemetryRum, globalAttributes, screenNameTracker);
170177

171178
NullPointerException exception = new NullPointerException("oopsie");
172179
Attributes attributes = Attributes.of(stringKey("one"), "1", longKey("two"), 2L);
@@ -197,7 +204,7 @@ private OpenTelemetrySdk buildTestSdk(InMemorySpanExporter testExporter) {
197204
void createAndEnd() {
198205
when(openTelemetryRum.getOpenTelemetry()).thenReturn(otelTesting.getOpenTelemetry());
199206

200-
SplunkRum splunkRum = new SplunkRum(openTelemetryRum, globalAttributes);
207+
SplunkRum splunkRum = new SplunkRum(openTelemetryRum, globalAttributes, screenNameTracker);
201208

202209
Span span = splunkRum.startWorkflow("workflow");
203210
Span inner = tracer.spanBuilder("foo").startSpan();
@@ -255,7 +262,7 @@ void updateLocation() {
255262
.when(globalAttributes)
256263
.update(isA(Consumer.class));
257264

258-
SplunkRum splunkRum = new SplunkRum(openTelemetryRum, globalAttributes);
265+
SplunkRum splunkRum = new SplunkRum(openTelemetryRum, globalAttributes, screenNameTracker);
259266

260267
Location location = mock(Location.class);
261268
when(location.getLatitude()).thenReturn(42d);
@@ -274,4 +281,37 @@ void updateLocation() {
274281

275282
assertTrue(updatedAttributes.get().isEmpty());
276283
}
284+
285+
@Test
286+
void canSetScreenName() {
287+
when(openTelemetryRum.getOpenTelemetry()).thenReturn(otelTesting.getOpenTelemetry());
288+
289+
SplunkRum splunkRum = new SplunkRum(openTelemetryRum, globalAttributes, screenNameTracker);
290+
splunkRum.setScreenName("screen-1");
291+
splunkRum.setScreenName("screen-2");
292+
293+
// pause and resume
294+
splunkRum.setScreenName(null);
295+
splunkRum.setScreenName("screen-2", "Resumed");
296+
297+
// exit the view with explicit screen names
298+
// both last screen name and second last screen name have to be cleared, hence the doubled call
299+
splunkRum.setScreenName(null);
300+
splunkRum.setScreenName(null);
301+
302+
List<SpanData> spans = otelTesting.getSpans();
303+
assertEquals(3, spans.size());
304+
305+
assertEquals("Created", spans.get(0).getName());
306+
assertEquals("screen-1", spans.get(0).getAttributes().get(SCREEN_NAME_KEY));
307+
assertNull(spans.get(0).getAttributes().get(LAST_SCREEN_NAME_KEY));
308+
309+
assertEquals("Created", spans.get(1).getName());
310+
assertEquals("screen-2", spans.get(1).getAttributes().get(SCREEN_NAME_KEY));
311+
assertEquals("screen-1", spans.get(1).getAttributes().get(LAST_SCREEN_NAME_KEY));
312+
313+
assertEquals("Resumed", spans.get(2).getName());
314+
assertEquals("screen-2", spans.get(2).getAttributes().get(SCREEN_NAME_KEY));
315+
assertNull(spans.get(2).getAttributes().get(LAST_SCREEN_NAME_KEY));
316+
}
277317
}

0 commit comments

Comments
 (0)