From cab74fb396180af39b27b697d28e740e3e9b19a7 Mon Sep 17 00:00:00 2001 From: Jason Plumb Date: Thu, 13 Jun 2024 15:11:19 -0700 Subject: [PATCH 1/2] add new experimental api to customize the currently visible screen name. --- .../android/sample/SampleApplication.java | 2 + .../java/com/splunk/rum/RumInitializer.java | 26 ++++++++++ .../java/com/splunk/rum/SplunkRumBuilder.java | 27 ++++++++++ .../incubating/CurrentlyVisibleScreen.java | 10 ++++ ...lyVisibleScreenAttributeSpanProcessor.java | 51 +++++++++++++++++++ .../com/splunk/rum/RumInitializerTest.java | 36 +++++++++++++ 6 files changed, 152 insertions(+) create mode 100644 splunk-otel-android/src/main/java/com/splunk/rum/incubating/CurrentlyVisibleScreen.java create mode 100644 splunk-otel-android/src/main/java/com/splunk/rum/incubating/internal/CurrentlyVisibleScreenAttributeSpanProcessor.java diff --git a/sample-app/src/main/java/com/splunk/android/sample/SampleApplication.java b/sample-app/src/main/java/com/splunk/android/sample/SampleApplication.java index a369eda0..827aedca 100644 --- a/sample-app/src/main/java/com/splunk/android/sample/SampleApplication.java +++ b/sample-app/src/main/java/com/splunk/android/sample/SampleApplication.java @@ -80,6 +80,8 @@ public void onCreate() { return chain.proceed(requestBuilder.build()); }); }) + .setExperimentalCurrentScreenCustomizer(vs -> + () -> "customized: " + vs.get()) .build(this); } } diff --git a/splunk-otel-android/src/main/java/com/splunk/rum/RumInitializer.java b/splunk-otel-android/src/main/java/com/splunk/rum/RumInitializer.java index 62bd5c70..8f78ac81 100644 --- a/splunk-otel-android/src/main/java/com/splunk/rum/RumInitializer.java +++ b/splunk-otel-android/src/main/java/com/splunk/rum/RumInitializer.java @@ -31,6 +31,9 @@ import android.os.Looper; import androidx.annotation.NonNull; import androidx.annotation.Nullable; + +import com.splunk.rum.incubating.CurrentlyVisibleScreen; +import com.splunk.rum.incubating.internal.CurrentlyVisibleScreenAttributeSpanProcessor; import com.splunk.rum.internal.GlobalAttributesSupplier; import com.splunk.rum.internal.UInt32QuadXorTraceIdRatioSampler; import io.opentelemetry.android.OpenTelemetryRum; @@ -52,6 +55,7 @@ import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.resources.ResourceBuilder; import io.opentelemetry.sdk.trace.SpanLimits; +import io.opentelemetry.sdk.trace.SpanProcessor; import io.opentelemetry.sdk.trace.data.SpanData; import io.opentelemetry.sdk.trace.export.BatchSpanProcessor; import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; @@ -97,6 +101,9 @@ SplunkRum initialize(Looper mainLooper) { if (!builder.isNetworkMonitorEnabled()) { config.disableNetworkChangeMonitoring(); } + if(builder.hasExperimentalVisibleScreenCustomization()){ + config.disableScreenAttributes(); + } OpenTelemetryRumBuilder otelRumBuilder = OpenTelemetryRum.builder(application, config); @@ -193,6 +200,10 @@ SplunkRum initialize(Looper mainLooper) { installCrashReporter(otelRumBuilder); } + if(builder.hasExperimentalVisibleScreenCustomization()){ + customizeCurrentlyVisibleScreen(otelRumBuilder, visibleScreenTracker); + } + // Lifecycle events instrumentation are always installed. installLifecycleInstrumentations(otelRumBuilder, visibleScreenTracker); @@ -207,6 +218,21 @@ SplunkRum initialize(Looper mainLooper) { return new SplunkRum(openTelemetryRum, globalAttributeSupplier); } + private void customizeCurrentlyVisibleScreen(OpenTelemetryRumBuilder otelRumBuilder, VisibleScreenTracker visibleScreenTracker) { + if(builder.experimentalCurrentScreenCustomizer == null){ + return; + } + Function customizer = builder.experimentalCurrentScreenCustomizer; + otelRumBuilder.addTracerProviderCustomizer( + (tracerProviderBuilder, app) -> { + CurrentlyVisibleScreen visibleScreen = visibleScreenTracker::getCurrentlyVisibleScreen; + CurrentlyVisibleScreen customized = customizer.apply(visibleScreen); + SpanProcessor screenAttributesAppender = + new CurrentlyVisibleScreenAttributeSpanProcessor(customized); + return tracerProviderBuilder.addSpanProcessor(screenAttributesAppender); + }); + } + @NonNull private MemorySpanBuffer constructBacklogProvider(VisibleScreenTracker visibleScreenTracker) { if (builder.isBackgroundInstrumentationDeferredUntilForeground()) { diff --git a/splunk-otel-android/src/main/java/com/splunk/rum/SplunkRumBuilder.java b/splunk-otel-android/src/main/java/com/splunk/rum/SplunkRumBuilder.java index ead999bb..04e6b81d 100644 --- a/splunk-otel-android/src/main/java/com/splunk/rum/SplunkRumBuilder.java +++ b/splunk-otel-android/src/main/java/com/splunk/rum/SplunkRumBuilder.java @@ -21,11 +21,14 @@ import android.app.Application; import android.util.Log; import androidx.annotation.Nullable; + +import com.splunk.rum.incubating.CurrentlyVisibleScreen; import com.splunk.rum.incubating.HttpSenderCustomizer; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.sdk.trace.export.SpanExporter; import java.time.Duration; import java.util.function.Consumer; +import java.util.function.Function; /** A builder of {@link SplunkRum}. */ public final class SplunkRumBuilder { @@ -50,6 +53,9 @@ public final class SplunkRumBuilder { double sessionBasedSamplerRatio = 1.0; boolean isSubprocess = false; + @Nullable + Function experimentalCurrentScreenCustomizer; + /** * Sets the application name that will be used to identify your application in the Splunk RUM * UI. @@ -394,6 +400,23 @@ public SplunkRumBuilder enableExperimentalOtlpExporter() { return this; } + /** + * This method can be used to provide a customer that is able of altering the + * value of the "current screen". Typically, SplunkRum will track Fragment and + * Activity state changes to automatically determine the name of the current "screen". + * By passing a customizer Function here, you can override the default behavior + * by customizing (at initialization time) the instance of CurrentlyVisibleScreen + * that will be used when setting the screen.name attribute on telemetry. + * Note that this does not alter the screen.name assigned to spans created by + * the ActivityTracer and FragmentTracer. Those will continue to mirror the name + * of the Activity or Fragment. + * Status: Experimental. API is subject to potential change or removal in the future. + */ + public SplunkRumBuilder setExperimentalCurrentScreenCustomizer(Function customizer){ + this.experimentalCurrentScreenCustomizer = customizer; + return this; + } + // one day maybe these can use kotlin delegation ConfigFlags getConfigFlags() { return configFlags; @@ -444,4 +467,8 @@ boolean isSubprocessInstrumentationDisabled() { boolean isBackgroundInstrumentationDeferredUntilForeground() { return configFlags.isBackgroundInstrumentationDeferredUntilForeground(); } + + public boolean hasExperimentalVisibleScreenCustomization() { + return experimentalCurrentScreenCustomizer != null; + } } diff --git a/splunk-otel-android/src/main/java/com/splunk/rum/incubating/CurrentlyVisibleScreen.java b/splunk-otel-android/src/main/java/com/splunk/rum/incubating/CurrentlyVisibleScreen.java new file mode 100644 index 00000000..6a75cf54 --- /dev/null +++ b/splunk-otel-android/src/main/java/com/splunk/rum/incubating/CurrentlyVisibleScreen.java @@ -0,0 +1,10 @@ +package com.splunk.rum.incubating; + +import java.util.function.Supplier; + +/** + * Experimental interface, used to return the name of the currently visible screen. + */ +public interface CurrentlyVisibleScreen extends Supplier { + +} diff --git a/splunk-otel-android/src/main/java/com/splunk/rum/incubating/internal/CurrentlyVisibleScreenAttributeSpanProcessor.java b/splunk-otel-android/src/main/java/com/splunk/rum/incubating/internal/CurrentlyVisibleScreenAttributeSpanProcessor.java new file mode 100644 index 00000000..4bedab73 --- /dev/null +++ b/splunk-otel-android/src/main/java/com/splunk/rum/incubating/internal/CurrentlyVisibleScreenAttributeSpanProcessor.java @@ -0,0 +1,51 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.splunk.rum.incubating.internal; + +import static io.opentelemetry.android.RumConstants.SCREEN_NAME_KEY; + +import com.splunk.rum.incubating.CurrentlyVisibleScreen; + +import io.opentelemetry.context.Context; +import io.opentelemetry.sdk.trace.ReadWriteSpan; +import io.opentelemetry.sdk.trace.ReadableSpan; +import io.opentelemetry.sdk.trace.SpanProcessor; + +/** + * Experimental SpanProcessor that sets the screen.name attribute on all + * Spans. It obtains the screen name from an instance of CurrentlyVisibleScreen, + * and exists primarily as a copy of ScreenAttributesSpanProcessor from upstream, + * without the problems of coupling to the VisibleScreenTracker. + */ +public final class CurrentlyVisibleScreenAttributeSpanProcessor implements SpanProcessor { + + private final CurrentlyVisibleScreen visibleScreen; + + public CurrentlyVisibleScreenAttributeSpanProcessor(CurrentlyVisibleScreen visibleScreen) { + this.visibleScreen = visibleScreen; + } + + @Override + public void onStart(Context parentContext, ReadWriteSpan span) { + String currentScreen = visibleScreen.get(); + span.setAttribute(SCREEN_NAME_KEY, currentScreen); + } + + @Override + public boolean isStartRequired() { + return true; + } + + @Override + public void onEnd(ReadableSpan span) { + // nop + } + + @Override + public boolean isEndRequired() { + return false; + } +} diff --git a/splunk-otel-android/src/test/java/com/splunk/rum/RumInitializerTest.java b/splunk-otel-android/src/test/java/com/splunk/rum/RumInitializerTest.java index b86d2733..e40ad93e 100644 --- a/splunk-otel-android/src/test/java/com/splunk/rum/RumInitializerTest.java +++ b/splunk-otel-android/src/test/java/com/splunk/rum/RumInitializerTest.java @@ -32,6 +32,8 @@ import android.content.Context; import android.content.pm.ApplicationInfo; import android.os.Looper; + +import com.splunk.rum.incubating.CurrentlyVisibleScreen; import com.splunk.rum.incubating.HttpSenderCustomizer; import io.opentelemetry.android.instrumentation.activity.VisibleScreenTracker; import io.opentelemetry.android.instrumentation.network.CurrentNetwork; @@ -52,6 +54,8 @@ import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Function; + import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; @@ -258,6 +262,38 @@ void canCustomizeHttpSender() { assertNotNull(seenBuilder.get()); } + @Test + void canCustomizeCurrentScreenName(){ + InMemorySpanExporter testExporter = InMemorySpanExporter.create(); + Function customizer = cvs -> () -> "custom:" + cvs.get(); + SplunkRumBuilder splunkRumBuilder = + new SplunkRumBuilder() + .setRealm("us0") + .setRumAccessToken("secret!") + .setApplicationName("test") + .disableAnrDetection() + .setExperimentalCurrentScreenCustomizer(customizer); + + when(application.getApplicationContext()).thenReturn(context); + when(application.getMainLooper()).thenReturn(mainLooper); + + RumInitializer testInitializer = + new RumInitializer(splunkRumBuilder, application, new AppStartupTimer()){ + @Override + SpanExporter getCoreSpanExporter() { + return testExporter; + } + }; + SplunkRum rum = testInitializer.initialize(mainLooper); + rum.addRumEvent("foo", Attributes.empty()); // need to trigger export + rum.flushSpans(); + + List spans = testExporter.getFinishedSpanItems(); + assertThat(spans.size()).isEqualTo(1); + assertThat(spans.get(0).getName()).isEqualTo("foo"); + assertThat(spans.get(0).getAttributes().get(stringKey("screen.name"))).isEqualTo("custom:unknown"); + } + @Test void shouldTranslateExceptionEventsToSpanAttributes() { InMemorySpanExporter spanExporter = InMemorySpanExporter.create(); From 3fd7ebea995e67eef8ef4ab84ffa712edad7fdce Mon Sep 17 00:00:00 2001 From: Jason Plumb Date: Thu, 13 Jun 2024 15:27:08 -0700 Subject: [PATCH 2/2] spotless --- .../android/sample/SampleApplication.java | 3 +-- .../java/com/splunk/rum/RumInitializer.java | 16 +++++++------ .../java/com/splunk/rum/SplunkRumBuilder.java | 22 ++++++++--------- .../incubating/CurrentlyVisibleScreen.java | 24 ++++++++++++++----- ...lyVisibleScreenAttributeSpanProcessor.java | 24 +++++++++++++------ .../com/splunk/rum/RumInitializerTest.java | 12 +++++----- 6 files changed, 61 insertions(+), 40 deletions(-) diff --git a/sample-app/src/main/java/com/splunk/android/sample/SampleApplication.java b/sample-app/src/main/java/com/splunk/android/sample/SampleApplication.java index 827aedca..ae1badad 100644 --- a/sample-app/src/main/java/com/splunk/android/sample/SampleApplication.java +++ b/sample-app/src/main/java/com/splunk/android/sample/SampleApplication.java @@ -80,8 +80,7 @@ public void onCreate() { return chain.proceed(requestBuilder.build()); }); }) - .setExperimentalCurrentScreenCustomizer(vs -> - () -> "customized: " + vs.get()) + .setExperimentalCurrentScreenCustomizer(vs -> () -> "customized: " + vs.get()) .build(this); } } diff --git a/splunk-otel-android/src/main/java/com/splunk/rum/RumInitializer.java b/splunk-otel-android/src/main/java/com/splunk/rum/RumInitializer.java index 8f78ac81..51ca2f1d 100644 --- a/splunk-otel-android/src/main/java/com/splunk/rum/RumInitializer.java +++ b/splunk-otel-android/src/main/java/com/splunk/rum/RumInitializer.java @@ -31,7 +31,6 @@ import android.os.Looper; import androidx.annotation.NonNull; import androidx.annotation.Nullable; - import com.splunk.rum.incubating.CurrentlyVisibleScreen; import com.splunk.rum.incubating.internal.CurrentlyVisibleScreenAttributeSpanProcessor; import com.splunk.rum.internal.GlobalAttributesSupplier; @@ -101,7 +100,7 @@ SplunkRum initialize(Looper mainLooper) { if (!builder.isNetworkMonitorEnabled()) { config.disableNetworkChangeMonitoring(); } - if(builder.hasExperimentalVisibleScreenCustomization()){ + if (builder.hasExperimentalVisibleScreenCustomization()) { config.disableScreenAttributes(); } @@ -200,7 +199,7 @@ SplunkRum initialize(Looper mainLooper) { installCrashReporter(otelRumBuilder); } - if(builder.hasExperimentalVisibleScreenCustomization()){ + if (builder.hasExperimentalVisibleScreenCustomization()) { customizeCurrentlyVisibleScreen(otelRumBuilder, visibleScreenTracker); } @@ -218,14 +217,17 @@ SplunkRum initialize(Looper mainLooper) { return new SplunkRum(openTelemetryRum, globalAttributeSupplier); } - private void customizeCurrentlyVisibleScreen(OpenTelemetryRumBuilder otelRumBuilder, VisibleScreenTracker visibleScreenTracker) { - if(builder.experimentalCurrentScreenCustomizer == null){ + private void customizeCurrentlyVisibleScreen( + OpenTelemetryRumBuilder otelRumBuilder, VisibleScreenTracker visibleScreenTracker) { + if (builder.experimentalCurrentScreenCustomizer == null) { return; } - Function customizer = builder.experimentalCurrentScreenCustomizer; + Function customizer = + builder.experimentalCurrentScreenCustomizer; otelRumBuilder.addTracerProviderCustomizer( (tracerProviderBuilder, app) -> { - CurrentlyVisibleScreen visibleScreen = visibleScreenTracker::getCurrentlyVisibleScreen; + CurrentlyVisibleScreen visibleScreen = + visibleScreenTracker::getCurrentlyVisibleScreen; CurrentlyVisibleScreen customized = customizer.apply(visibleScreen); SpanProcessor screenAttributesAppender = new CurrentlyVisibleScreenAttributeSpanProcessor(customized); diff --git a/splunk-otel-android/src/main/java/com/splunk/rum/SplunkRumBuilder.java b/splunk-otel-android/src/main/java/com/splunk/rum/SplunkRumBuilder.java index 04e6b81d..1ee5c16c 100644 --- a/splunk-otel-android/src/main/java/com/splunk/rum/SplunkRumBuilder.java +++ b/splunk-otel-android/src/main/java/com/splunk/rum/SplunkRumBuilder.java @@ -21,7 +21,6 @@ import android.app.Application; import android.util.Log; import androidx.annotation.Nullable; - import com.splunk.rum.incubating.CurrentlyVisibleScreen; import com.splunk.rum.incubating.HttpSenderCustomizer; import io.opentelemetry.api.common.Attributes; @@ -401,18 +400,17 @@ public SplunkRumBuilder enableExperimentalOtlpExporter() { } /** - * This method can be used to provide a customer that is able of altering the - * value of the "current screen". Typically, SplunkRum will track Fragment and - * Activity state changes to automatically determine the name of the current "screen". - * By passing a customizer Function here, you can override the default behavior - * by customizing (at initialization time) the instance of CurrentlyVisibleScreen - * that will be used when setting the screen.name attribute on telemetry. - * Note that this does not alter the screen.name assigned to spans created by - * the ActivityTracer and FragmentTracer. Those will continue to mirror the name - * of the Activity or Fragment. - * Status: Experimental. API is subject to potential change or removal in the future. + * This method can be used to provide a customer that is able of altering the value of the + * "current screen". Typically, SplunkRum will track Fragment and Activity state changes to + * automatically determine the name of the current "screen". By passing a customizer Function + * here, you can override the default behavior by customizing (at initialization time) the + * instance of CurrentlyVisibleScreen that will be used when setting the screen.name attribute + * on telemetry. Note that this does not alter the screen.name assigned to spans created by the + * ActivityTracer and FragmentTracer. Those will continue to mirror the name of the Activity or + * Fragment. Status: Experimental. API is subject to potential change or removal in the future. */ - public SplunkRumBuilder setExperimentalCurrentScreenCustomizer(Function customizer){ + public SplunkRumBuilder setExperimentalCurrentScreenCustomizer( + Function customizer) { this.experimentalCurrentScreenCustomizer = customizer; return this; } diff --git a/splunk-otel-android/src/main/java/com/splunk/rum/incubating/CurrentlyVisibleScreen.java b/splunk-otel-android/src/main/java/com/splunk/rum/incubating/CurrentlyVisibleScreen.java index 6a75cf54..d4b0bad8 100644 --- a/splunk-otel-android/src/main/java/com/splunk/rum/incubating/CurrentlyVisibleScreen.java +++ b/splunk-otel-android/src/main/java/com/splunk/rum/incubating/CurrentlyVisibleScreen.java @@ -1,10 +1,22 @@ +/* + * Copyright Splunk Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.splunk.rum.incubating; import java.util.function.Supplier; -/** - * Experimental interface, used to return the name of the currently visible screen. - */ -public interface CurrentlyVisibleScreen extends Supplier { - -} +/** Experimental interface, used to return the name of the currently visible screen. */ +public interface CurrentlyVisibleScreen extends Supplier {} diff --git a/splunk-otel-android/src/main/java/com/splunk/rum/incubating/internal/CurrentlyVisibleScreenAttributeSpanProcessor.java b/splunk-otel-android/src/main/java/com/splunk/rum/incubating/internal/CurrentlyVisibleScreenAttributeSpanProcessor.java index 4bedab73..3a1e5779 100644 --- a/splunk-otel-android/src/main/java/com/splunk/rum/incubating/internal/CurrentlyVisibleScreenAttributeSpanProcessor.java +++ b/splunk-otel-android/src/main/java/com/splunk/rum/incubating/internal/CurrentlyVisibleScreenAttributeSpanProcessor.java @@ -1,6 +1,17 @@ /* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 + * Copyright Splunk Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package com.splunk.rum.incubating.internal; @@ -8,17 +19,16 @@ import static io.opentelemetry.android.RumConstants.SCREEN_NAME_KEY; import com.splunk.rum.incubating.CurrentlyVisibleScreen; - import io.opentelemetry.context.Context; import io.opentelemetry.sdk.trace.ReadWriteSpan; import io.opentelemetry.sdk.trace.ReadableSpan; import io.opentelemetry.sdk.trace.SpanProcessor; /** - * Experimental SpanProcessor that sets the screen.name attribute on all - * Spans. It obtains the screen name from an instance of CurrentlyVisibleScreen, - * and exists primarily as a copy of ScreenAttributesSpanProcessor from upstream, - * without the problems of coupling to the VisibleScreenTracker. + * Experimental SpanProcessor that sets the screen.name attribute on all Spans. It obtains the + * screen name from an instance of CurrentlyVisibleScreen, and exists primarily as a copy of + * ScreenAttributesSpanProcessor from upstream, without the problems of coupling to the + * VisibleScreenTracker. */ public final class CurrentlyVisibleScreenAttributeSpanProcessor implements SpanProcessor { diff --git a/splunk-otel-android/src/test/java/com/splunk/rum/RumInitializerTest.java b/splunk-otel-android/src/test/java/com/splunk/rum/RumInitializerTest.java index e40ad93e..ead40093 100644 --- a/splunk-otel-android/src/test/java/com/splunk/rum/RumInitializerTest.java +++ b/splunk-otel-android/src/test/java/com/splunk/rum/RumInitializerTest.java @@ -32,7 +32,6 @@ import android.content.Context; import android.content.pm.ApplicationInfo; import android.os.Looper; - import com.splunk.rum.incubating.CurrentlyVisibleScreen; import com.splunk.rum.incubating.HttpSenderCustomizer; import io.opentelemetry.android.instrumentation.activity.VisibleScreenTracker; @@ -55,7 +54,6 @@ import java.util.List; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; - import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; @@ -263,9 +261,10 @@ void canCustomizeHttpSender() { } @Test - void canCustomizeCurrentScreenName(){ + void canCustomizeCurrentScreenName() { InMemorySpanExporter testExporter = InMemorySpanExporter.create(); - Function customizer = cvs -> () -> "custom:" + cvs.get(); + Function customizer = + cvs -> () -> "custom:" + cvs.get(); SplunkRumBuilder splunkRumBuilder = new SplunkRumBuilder() .setRealm("us0") @@ -278,7 +277,7 @@ void canCustomizeCurrentScreenName(){ when(application.getMainLooper()).thenReturn(mainLooper); RumInitializer testInitializer = - new RumInitializer(splunkRumBuilder, application, new AppStartupTimer()){ + new RumInitializer(splunkRumBuilder, application, new AppStartupTimer()) { @Override SpanExporter getCoreSpanExporter() { return testExporter; @@ -291,7 +290,8 @@ SpanExporter getCoreSpanExporter() { List spans = testExporter.getFinishedSpanItems(); assertThat(spans.size()).isEqualTo(1); assertThat(spans.get(0).getName()).isEqualTo("foo"); - assertThat(spans.get(0).getAttributes().get(stringKey("screen.name"))).isEqualTo("custom:unknown"); + assertThat(spans.get(0).getAttributes().get(stringKey("screen.name"))) + .isEqualTo("custom:unknown"); } @Test