Skip to content

Commit

Permalink
Decouple global span attributes from RumAttributeAppender (#375)
Browse files Browse the repository at this point in the history
* Decouple global span attributes from RumAttributeAppender

* Moved OTel stuff to io.opentelemetry.rum.internal
  • Loading branch information
Mateusz Rzeszutek authored Oct 12, 2022
1 parent 9bef49c commit b3e540e
Show file tree
Hide file tree
Showing 10 changed files with 204 additions and 81 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,11 @@
import android.os.Build;
import androidx.annotation.Nullable;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.sdk.trace.ReadWriteSpan;
import io.opentelemetry.sdk.trace.ReadableSpan;
import io.opentelemetry.sdk.trace.SpanProcessor;
import java.util.function.Supplier;

class RumAttributeAppender implements SpanProcessor {
static final AttributeKey<String> APP_NAME_KEY = stringKey("app");
Expand All @@ -48,21 +46,18 @@ class RumAttributeAppender implements SpanProcessor {
static final AttributeKey<String> SPLUNK_OPERATION_KEY = stringKey("_splunk_operation");

private final String applicationName;
private final Supplier<Attributes> globalAttributesSupplier;
private final SessionId sessionId;
private final String rumVersion;
private final VisibleScreenTracker visibleScreenTracker;
private final ConnectionUtil connectionUtil;

RumAttributeAppender(
String applicationName,
Supplier<Attributes> globalAttributesSupplier,
SessionId sessionId,
String rumVersion,
VisibleScreenTracker visibleScreenTracker,
ConnectionUtil connectionUtil) {
this.applicationName = applicationName;
this.globalAttributesSupplier = globalAttributesSupplier;
this.sessionId = sessionId;
this.rumVersion = rumVersion;
this.visibleScreenTracker = visibleScreenTracker;
Expand All @@ -84,7 +79,6 @@ public void onStart(Context parentContext, ReadWriteSpan span) {
span.setAttribute(OS_NAME, "Android");
span.setAttribute(OS_TYPE, "linux");
span.setAttribute(OS_VERSION, Build.VERSION.RELEASE);
span.setAllAttributes(globalAttributesSupplier.get());

String currentScreen = visibleScreenTracker.getCurrentlyVisibleScreen();
span.setAttribute(SplunkRum.SCREEN_NAME_KEY, currentScreen);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,20 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.splunk.android.rum.R;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Context;
import io.opentelemetry.exporter.logging.LoggingSpanExporter;
import io.opentelemetry.exporter.zipkin.ZipkinSpanExporter;
import io.opentelemetry.rum.internal.GlobalAttributesSpanAppender;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.common.Clock;
import io.opentelemetry.sdk.common.CompletableResultCode;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder;
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;
Expand All @@ -53,7 +54,6 @@
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
import java.util.logging.Level;
import zipkin2.reporter.Sender;
Expand All @@ -66,7 +66,6 @@ class RumInitializer {
static final int MAX_ATTRIBUTE_LENGTH = 256 * 128;

private final SplunkRumBuilder builder;
private final AtomicReference<Attributes> globalAttributes;
private final Application application;
private final AppStartupTimer startupTimer;
private final List<RumInitializer.InitializationEvent> initializationEvents = new ArrayList<>();
Expand All @@ -75,7 +74,6 @@ class RumInitializer {
RumInitializer(
SplunkRumBuilder builder, Application application, AppStartupTimer startupTimer) {
this.builder = builder;
this.globalAttributes = builder.buildGlobalAttributesRef();
this.application = application;
this.startupTimer = startupTimer;
this.timingClock = startupTimer.startupClock;
Expand All @@ -102,14 +100,17 @@ SplunkRum initialize(ConnectionUtil.Factory connectionUtilFactory, Looper mainLo
initializationEvents.add(
new RumInitializer.InitializationEvent("sessionIdInitialized", timingClock.now()));

GlobalAttributesSpanAppender globalAttributesSpanAppender =
GlobalAttributesSpanAppender.create(builder.buildInitialGlobalAttributes());
SdkTracerProvider sdkTracerProvider =
buildTracerProvider(
Clock.getDefault(),
zipkinExporter,
sessionId,
rumVersion,
visibleScreenTracker,
connectionUtil);
connectionUtil,
globalAttributesSpanAppender);
initializationEvents.add(
new RumInitializer.InitializationEvent(
"tracerProviderInitialized", timingClock.now()));
Expand Down Expand Up @@ -173,7 +174,7 @@ SplunkRum initialize(ConnectionUtil.Factory connectionUtilFactory, Looper mainLo

recordInitializationSpans(startTimeNanos, initializationEvents, tracer);

return new SplunkRum(openTelemetrySdk, sessionId, globalAttributes);
return new SplunkRum(openTelemetrySdk, sessionId, globalAttributesSpanAppender);
}

private SlowRenderingDetector buildSlowRenderingDetector(Tracer tracer) {
Expand Down Expand Up @@ -277,7 +278,8 @@ private SdkTracerProvider buildTracerProvider(
SessionId sessionId,
String rumVersion,
VisibleScreenTracker visibleScreenTracker,
ConnectionUtil connectionUtil) {
ConnectionUtil connectionUtil,
SpanProcessor... additionalProcessors) {
BatchSpanProcessor batchSpanProcessor = BatchSpanProcessor.builder(zipkinExporter).build();
initializationEvents.add(
new RumInitializer.InitializationEvent(
Expand All @@ -287,7 +289,6 @@ private SdkTracerProvider buildTracerProvider(
RumAttributeAppender attributeAppender =
new RumAttributeAppender(
applicationName,
globalAttributes::get,
sessionId,
rumVersion,
visibleScreenTracker,
Expand Down Expand Up @@ -320,6 +321,10 @@ private SdkTracerProvider buildTracerProvider(
new SessionIdRatioBasedSampler(builder.sessionBasedSamplerRatio, sessionId));
}

for (SpanProcessor spanProcessor : additionalProcessors) {
tracerProviderBuilder.addSpanProcessor(spanProcessor);
}

if (builder.debugEnabled) {
tracerProviderBuilder.addSpanProcessor(
SimpleSpanProcessor.create(
Expand Down
24 changes: 5 additions & 19 deletions splunk-otel-android/src/main/java/com/splunk/rum/SplunkRum.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import static io.opentelemetry.api.common.AttributeKey.doubleKey;
import static io.opentelemetry.api.common.AttributeKey.longKey;
import static io.opentelemetry.api.common.AttributeKey.stringKey;
import static java.util.Objects.requireNonNull;

import android.app.Application;
import android.location.Location;
Expand All @@ -36,10 +35,10 @@
import io.opentelemetry.api.trace.StatusCode;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.instrumentation.okhttp.v3_0.OkHttpTelemetry;
import io.opentelemetry.rum.internal.GlobalAttributesSpanAppender;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import okhttp3.Call;
import okhttp3.OkHttpClient;
Expand Down Expand Up @@ -79,7 +78,7 @@ public class SplunkRum {

private final SessionId sessionId;
private final OpenTelemetrySdk openTelemetrySdk;
private final AtomicReference<Attributes> globalAttributes;
private final GlobalAttributesSpanAppender globalAttributes;

static {
Handler handler = new Handler(Looper.getMainLooper());
Expand All @@ -89,7 +88,7 @@ public class SplunkRum {
SplunkRum(
OpenTelemetrySdk openTelemetrySdk,
SessionId sessionId,
AtomicReference<Attributes> globalAttributes) {
GlobalAttributesSpanAppender globalAttributes) {
this.openTelemetrySdk = openTelemetrySdk;
this.sessionId = sessionId;
this.globalAttributes = globalAttributes;
Expand Down Expand Up @@ -280,11 +279,9 @@ private String formatStackTrace(StackTraceElement[] stackTrace) {
* @param key The {@link AttributeKey} for the attribute.
* @param value The value of the attribute, which must match the generic type of the key.
* @param <T> The generic type of the value.
* @return this.
*/
public <T> SplunkRum setGlobalAttribute(AttributeKey<T> key, T value) {
public <T> void setGlobalAttribute(AttributeKey<T> key, T value) {
updateGlobalAttributes(attributesBuilder -> attributesBuilder.put(key, value));
return this;
}

/**
Expand All @@ -297,18 +294,7 @@ public <T> SplunkRum setGlobalAttribute(AttributeKey<T> key, T value) {
* operating on a {@link AttributesBuilder} from the current set.
*/
public void updateGlobalAttributes(Consumer<AttributesBuilder> attributesUpdater) {
while (true) {
// we're absolutely certain this will never be null
Attributes oldAttributes = requireNonNull(globalAttributes.get());

AttributesBuilder builder = oldAttributes.toBuilder();
attributesUpdater.accept(builder);
Attributes newAttributes = builder.build();

if (globalAttributes.compareAndSet(oldAttributes, newAttributes)) {
break;
}
}
globalAttributes.update(attributesUpdater);
}

// for testing only
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
import io.opentelemetry.sdk.trace.export.SpanExporter;
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
import java.time.Duration;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;

/** A builder of {@link SplunkRum}. */
Expand Down Expand Up @@ -311,14 +310,14 @@ SpanExporter decorateWithSpanFilter(SpanExporter exporter) {
return spanFilterBuilder.build().apply(exporter);
}

AtomicReference<Attributes> buildGlobalAttributesRef() {
Attributes buildInitialGlobalAttributes() {
Attributes attrs = globalAttributes;
if (deploymentEnvironment != null) {
attrs =
attrs.toBuilder()
.put(ResourceAttributes.DEPLOYMENT_ENVIRONMENT, deploymentEnvironment)
.build();
}
return new AtomicReference<>(attrs);
return attrs;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* 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 io.opentelemetry.rum.internal;

import static java.util.Objects.requireNonNull;

import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.context.Context;
import io.opentelemetry.sdk.trace.ReadWriteSpan;
import io.opentelemetry.sdk.trace.ReadableSpan;
import io.opentelemetry.sdk.trace.SpanProcessor;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;

/**
* A {@link SpanProcessor} implementation that appends a set of {@linkplain Attributes attributes}
* to every span that is exported. The attributes collection is mutable, and can be updated by
* calling {@link #update(Consumer)}.
*
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
* at any time.
*/
public final class GlobalAttributesSpanAppender implements SpanProcessor {

/**
* Returns a new {@link GlobalAttributesSpanAppender} with a given initial attributes.
*
* @param initialState The initial collection of attributes to append to every span.
*/
public static GlobalAttributesSpanAppender create(Attributes initialState) {
return new GlobalAttributesSpanAppender(initialState);
}

private final AtomicReference<Attributes> attributes;

private GlobalAttributesSpanAppender(Attributes initialState) {
this.attributes = new AtomicReference<>(initialState);
}

@Override
public void onStart(Context parentContext, ReadWriteSpan span) {
span.setAllAttributes(attributes.get());
}

@Override
public boolean isStartRequired() {
return true;
}

@Override
public void onEnd(ReadableSpan span) {}

@Override
public boolean isEndRequired() {
return false;
}

/**
* Update the global set of attributes that will be appended to every span.
*
* <p>Note: this operation performs an atomic update. The passed function should be free from
* side effects, since it may be called multiple times in case of thread contention.
*
* @param attributesUpdater A function which will update the current set of attributes, by
* operating on a {@link AttributesBuilder} from the current set.
*/
public void update(Consumer<AttributesBuilder> attributesUpdater) {
while (true) {
// we're absolutely certain this will never be null
Attributes oldAttributes = requireNonNull(attributes.get());

AttributesBuilder builder = oldAttributes.toBuilder();
attributesUpdater.accept(builder);
Attributes newAttributes = builder.build();

if (attributes.compareAndSet(oldAttributes, newAttributes)) {
break;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public void doesNotThrow() {
assertSame(okHttpClient, instance.createRumOkHttpCallFactory(okHttpClient));

instance.updateGlobalAttributes(attributesBuilder -> {});
assertSame(instance, instance.setGlobalAttribute(AttributeKey.stringKey("foo"), "bar"));
instance.setGlobalAttribute(AttributeKey.stringKey("foo"), "bar");
instance.flushSpans();

instance.integrateWithBrowserRum(mock(WebView.class));
Expand Down
Loading

0 comments on commit b3e540e

Please sign in to comment.