Skip to content

Commit

Permalink
Decouple resource attributes and zipkin span name from RumAttributeAp…
Browse files Browse the repository at this point in the history
…pender (#376)
  • Loading branch information
Mateusz Rzeszutek authored Oct 18, 2022
1 parent 57f084a commit edfd561
Show file tree
Hide file tree
Showing 14 changed files with 426 additions and 277 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,8 @@
* We need a custom encoder to correct for the fact that the zipkin Span.Builder lowercases all Span
* names.
*
* <p>We do this by having the {@link RumAttributeAppender} add an additional attribute ({@link
* RumAttributeAppender#SPLUNK_OPERATION_KEY}) with the span name properly cased, then correcting
* the span name here at encoding time.
* <p>SplunkSpanDataModifier#SPLUNK_OPERATION_KEY}) with the span name properly cased, then
* correcting the span name here at encoding time.
*/
class CustomZipkinEncoder implements BytesEncoder<Span> {

Expand All @@ -49,7 +48,8 @@ public int sizeInBytes(Span span) {

@Override
public byte[] encode(Span span) {
String properSpanName = span.tags().get(RumAttributeAppender.SPLUNK_OPERATION_KEY.getKey());
String properSpanName =
span.tags().get(SplunkSpanDataModifier.SPLUNK_OPERATION_KEY.getKey());

// note: this can be optimized, if necessary. Let's keep it simple for now.
byte[] rawBytes = JsonCodec.write(this.writer, span);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,89 +16,19 @@

package com.splunk.rum;

import static com.splunk.rum.SplunkRum.ERROR_MESSAGE_KEY;
import static com.splunk.rum.SplunkRum.ERROR_TYPE_KEY;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.EXCEPTION_MESSAGE;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.EXCEPTION_STACKTRACE;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.EXCEPTION_TYPE;

import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.sdk.trace.data.DelegatingSpanData;
import io.opentelemetry.sdk.trace.data.EventData;
import io.opentelemetry.sdk.trace.data.SpanData;
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
import java.util.ArrayList;
import java.util.List;

final class ModifiedSpanData extends DelegatingSpanData {

private final List<EventData> modifiedEvents;
private final Attributes modifiedAttributes;

static SpanData create(SpanData original) {
return create(original, original.getAttributes().toBuilder());
}

static SpanData create(SpanData original, AttributesBuilder modifiedAttributes) {
// zipkin eats the event attributes that are recorded by default, so we need to convert
// the exception event to span attributes
List<EventData> modifiedEvents = new ArrayList<>(original.getEvents().size());
for (EventData event : original.getEvents()) {
if (event.getName().equals(SemanticAttributes.EXCEPTION_EVENT_NAME)) {
modifiedAttributes.putAll(extractExceptionAttributes(event));
} else {
// if it's not an exception, leave the event as it is
modifiedEvents.add(event);
}
}

return new ModifiedSpanData(original, modifiedEvents, modifiedAttributes.build());
}

private static Attributes extractExceptionAttributes(EventData event) {
String type = event.getAttributes().get(EXCEPTION_TYPE);
String message = event.getAttributes().get(EXCEPTION_MESSAGE);
String stacktrace = event.getAttributes().get(EXCEPTION_STACKTRACE);

AttributesBuilder builder = Attributes.builder();
if (type != null) {
int dot = type.lastIndexOf('.');
String simpleType = dot == -1 ? type : type.substring(dot + 1);
builder.put(EXCEPTION_TYPE, simpleType);
// this attribute's here to support the RUM UI/backend until it can be updated to use
// otel conventions.
builder.put(ERROR_TYPE_KEY, simpleType);
}
if (message != null) {
builder.put(EXCEPTION_MESSAGE, message);
// this attribute's here to support the RUM UI/backend until it can be updated to use
// otel conventions.
builder.put(ERROR_MESSAGE_KEY, message);
}
if (stacktrace != null) {
builder.put(EXCEPTION_STACKTRACE, stacktrace);
}
return builder.build();
}

ModifiedSpanData(
SpanData original, List<EventData> modifiedEvents, Attributes modifiedAttributes) {
ModifiedSpanData(SpanData original, Attributes modifiedAttributes) {
super(original);
this.modifiedEvents = modifiedEvents;
this.modifiedAttributes = modifiedAttributes;
}

@Override
public List<EventData> getEvents() {
return modifiedEvents;
}

@Override
public int getTotalRecordedEvents() {
return modifiedEvents.size();
}

@Override
public Attributes getAttributes() {
return modifiedAttributes;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,13 @@
package com.splunk.rum;

import static io.opentelemetry.api.common.AttributeKey.stringKey;
import static io.opentelemetry.semconv.resource.attributes.ResourceAttributes.DEVICE_MODEL_IDENTIFIER;
import static io.opentelemetry.semconv.resource.attributes.ResourceAttributes.DEVICE_MODEL_NAME;
import static io.opentelemetry.semconv.resource.attributes.ResourceAttributes.OS_NAME;
import static io.opentelemetry.semconv.resource.attributes.ResourceAttributes.OS_TYPE;
import static io.opentelemetry.semconv.resource.attributes.ResourceAttributes.OS_VERSION;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NET_HOST_CARRIER_ICC;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NET_HOST_CARRIER_MCC;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NET_HOST_CARRIER_MNC;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NET_HOST_CARRIER_NAME;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NET_HOST_CONNECTION_SUBTYPE;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NET_HOST_CONNECTION_TYPE;

import android.os.Build;
import androidx.annotation.Nullable;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.trace.Span;
Expand All @@ -39,49 +33,29 @@
import io.opentelemetry.sdk.trace.SpanProcessor;

class RumAttributeAppender implements SpanProcessor {
static final AttributeKey<String> APP_NAME_KEY = stringKey("app");
static final AttributeKey<String> SESSION_ID_KEY = stringKey("splunk.rumSessionId");
static final AttributeKey<String> RUM_VERSION_KEY = stringKey("splunk.rum.version");

static final AttributeKey<String> SPLUNK_OPERATION_KEY = stringKey("_splunk_operation");
static final AttributeKey<String> SESSION_ID_KEY = stringKey("splunk.rumSessionId");

private final String applicationName;
private final SessionId sessionId;
private final String rumVersion;
private final VisibleScreenTracker visibleScreenTracker;
private final ConnectionUtil connectionUtil;

RumAttributeAppender(
String applicationName,
SessionId sessionId,
String rumVersion,
VisibleScreenTracker visibleScreenTracker,
ConnectionUtil connectionUtil) {
this.applicationName = applicationName;
this.sessionId = sessionId;
this.rumVersion = rumVersion;
this.visibleScreenTracker = visibleScreenTracker;
this.connectionUtil = connectionUtil;
}

@Override
public void onStart(Context parentContext, ReadWriteSpan span) {
// set this custom attribute in order to let the CustomZipkinEncoder use it for the span
// name on the wire.
span.setAttribute(SPLUNK_OPERATION_KEY, span.getName());

span.setAttribute(APP_NAME_KEY, applicationName);
span.setAttribute(SESSION_ID_KEY, sessionId.getSessionId());
span.setAttribute(RUM_VERSION_KEY, rumVersion);

span.setAttribute(DEVICE_MODEL_NAME, Build.MODEL);
span.setAttribute(DEVICE_MODEL_IDENTIFIER, Build.MODEL);
span.setAttribute(OS_NAME, "Android");
span.setAttribute(OS_TYPE, "linux");
span.setAttribute(OS_VERSION, Build.VERSION.RELEASE);

String currentScreen = visibleScreenTracker.getCurrentlyVisibleScreen();
span.setAttribute(SplunkRum.SCREEN_NAME_KEY, currentScreen);

CurrentNetwork currentNetwork = connectionUtil.getActiveNetwork();
appendNetworkAttributes(span, currentNetwork);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,16 @@

package com.splunk.rum;

import static com.splunk.rum.SplunkRum.APP_NAME_KEY;
import static com.splunk.rum.SplunkRum.LOG_TAG;
import static com.splunk.rum.SplunkRum.RUM_VERSION_KEY;
import static io.opentelemetry.semconv.resource.attributes.ResourceAttributes.DEPLOYMENT_ENVIRONMENT;
import static io.opentelemetry.semconv.resource.attributes.ResourceAttributes.DEVICE_MODEL_IDENTIFIER;
import static io.opentelemetry.semconv.resource.attributes.ResourceAttributes.DEVICE_MODEL_NAME;
import static io.opentelemetry.semconv.resource.attributes.ResourceAttributes.OS_NAME;
import static io.opentelemetry.semconv.resource.attributes.ResourceAttributes.OS_TYPE;
import static io.opentelemetry.semconv.resource.attributes.ResourceAttributes.OS_VERSION;
import static io.opentelemetry.semconv.resource.attributes.ResourceAttributes.SERVICE_NAME;
import static java.util.Objects.requireNonNull;

import android.app.Application;
Expand All @@ -37,6 +46,7 @@
import io.opentelemetry.sdk.common.Clock;
import io.opentelemetry.sdk.common.CompletableResultCode;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.resources.ResourceBuilder;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder;
import io.opentelemetry.sdk.trace.SpanLimits;
Expand Down Expand Up @@ -101,7 +111,7 @@ SplunkRum initialize(ConnectionUtil.Factory connectionUtilFactory, Looper mainLo
new RumInitializer.InitializationEvent("sessionIdInitialized", timingClock.now()));

GlobalAttributesSpanAppender globalAttributesSpanAppender =
GlobalAttributesSpanAppender.create(builder.buildInitialGlobalAttributes());
GlobalAttributesSpanAppender.create(builder.globalAttributes);
SdkTracerProvider sdkTracerProvider =
buildTracerProvider(
Clock.getDefault(),
Expand Down Expand Up @@ -284,18 +294,12 @@ private SdkTracerProvider buildTracerProvider(

String applicationName = requireNonNull(builder.applicationName);
RumAttributeAppender attributeAppender =
new RumAttributeAppender(
applicationName,
sessionId,
rumVersion,
visibleScreenTracker,
connectionUtil);
new RumAttributeAppender(sessionId, visibleScreenTracker, connectionUtil);
initializationEvents.add(
new RumInitializer.InitializationEvent(
"attributeAppenderInitialized", timingClock.now()));

Resource resource =
Resource.getDefault().toBuilder().put("service.name", applicationName).build();
Resource resource = buildResource(applicationName, rumVersion);
initializationEvents.add(
new RumInitializer.InitializationEvent("resourceInitialized", timingClock.now()));

Expand Down Expand Up @@ -333,10 +337,29 @@ private SdkTracerProvider buildTracerProvider(
return tracerProviderBuilder.build();
}

private Resource buildResource(String applicationName, String rumVersion) {
ResourceBuilder resourceBuilder =
Resource.getDefault().toBuilder()
.put(APP_NAME_KEY, applicationName)
.put(SERVICE_NAME, applicationName);
if (builder.deploymentEnvironment != null) {
resourceBuilder.put(DEPLOYMENT_ENVIRONMENT, builder.deploymentEnvironment);
}
return resourceBuilder
.put(RUM_VERSION_KEY, rumVersion)
.put(DEVICE_MODEL_NAME, Build.MODEL)
.put(DEVICE_MODEL_IDENTIFIER, Build.MODEL)
.put(OS_NAME, "Android")
.put(OS_TYPE, "linux")
.put(OS_VERSION, Build.VERSION.RELEASE)
.build();
}

// visible for testing
SpanExporter buildFilteringExporter(ConnectionUtil connectionUtil) {
SpanExporter exporter = buildExporter(connectionUtil);
SpanExporter filteredExporter = builder.decorateWithSpanFilter(exporter);
SpanExporter splunkTranslatedExporter = new SplunkSpanDataModifier(exporter);
SpanExporter filteredExporter = builder.decorateWithSpanFilter(splunkTranslatedExporter);
initializationEvents.add(
new InitializationEvent("zipkin exporter initialized", timingClock.now()));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ private boolean reject(SpanData span) {

private SpanData modify(SpanData span) {
if (spanAttributeReplacements.isEmpty()) {
return ModifiedSpanData.create(span);
return span;
}

AttributesBuilder modifiedAttributes = Attributes.builder();
Expand All @@ -94,7 +94,7 @@ private SpanData modify(SpanData span) {
}
});

return ModifiedSpanData.create(span, modifiedAttributes);
return new ModifiedSpanData(span, modifiedAttributes.build());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ public class SplunkRum {
static final AttributeKey<String> LINK_TRACE_ID_KEY = stringKey("link.traceId");
static final AttributeKey<String> LINK_SPAN_ID_KEY = stringKey("link.spanId");

static final AttributeKey<String> APP_NAME_KEY = stringKey("app");
static final AttributeKey<String> RUM_VERSION_KEY = stringKey("splunk.rum.version");

@Nullable private static SplunkRum INSTANCE;

private final SessionId sessionId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import com.splunk.rum.reactnative.ReactNativeExporter;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.sdk.trace.export.SpanExporter;
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
import java.time.Duration;
import java.util.function.Consumer;

Expand All @@ -45,8 +44,8 @@ public final class SplunkRumBuilder {
boolean anrDetectionEnabled = true;
boolean slowRenderingDetectionEnabled = true;
Duration slowRenderingDetectionPollInterval = DEFAULT_SLOW_RENDERING_DETECTION_POLL_INTERVAL;
private Attributes globalAttributes = Attributes.empty();
@Nullable private String deploymentEnvironment;
Attributes globalAttributes = Attributes.empty();
@Nullable String deploymentEnvironment;
private final SpanFilterBuilder spanFilterBuilder = new SpanFilterBuilder();
int maxUsageMegabytes = DEFAULT_MAX_STORAGE_USE_MB;
boolean sessionBasedSamplerEnabled = false;
Expand Down Expand Up @@ -320,15 +319,4 @@ SpanExporter decorateWithSpanFilter(SpanExporter exporter) {
public SpanExporter decorateWithReactNativeExporter(SpanExporter exporter) {
return new ReactNativeExporter(exporter);
}

Attributes buildInitialGlobalAttributes() {
Attributes attrs = globalAttributes;
if (deploymentEnvironment != null) {
attrs =
attrs.toBuilder()
.put(ResourceAttributes.DEPLOYMENT_ENVIRONMENT, deploymentEnvironment)
.build();
}
return attrs;
}
}
Loading

0 comments on commit edfd561

Please sign in to comment.