Skip to content

Commit

Permalink
Adds a new API that can customize the ingest sender. (#742)
Browse files Browse the repository at this point in the history
* add a way to customize the sender.

* inline

* specify zipkin sender version
  • Loading branch information
breedx-splk authored Jan 31, 2024
1 parent a8b4d54 commit 47ab538
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 10 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

### Unreleased

* tbd
* Add new incubating API: `SplunkRumBuilder.setHttpSenderCustomizer()` to allow customization
of the HTTP client used for sending data to Splunk. This can be useful when devices are
behind a proxy or API gateway.

## Version 1.3.1 - 2023-12-14

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import io.opentelemetry.api.common.Attributes;
import java.time.Duration;
import java.util.regex.Pattern;
import okhttp3.Request;

public class SampleApplication extends Application {

Expand Down Expand Up @@ -65,6 +66,20 @@ public void onCreate() {
HTTP_URL_SENSITIVE_DATA_PATTERN
.matcher(value)
.replaceAll("$1=<redacted>")))
.setHttpSenderCustomizer(
okHttpBuilder -> {
okHttpBuilder.compressionEnabled(true);
okHttpBuilder
.clientBuilder()
.addInterceptor(
chain -> {
Request.Builder requestBuilder =
chain.request().newBuilder();
requestBuilder.header(
"X-My-Custom-Header", "abc123");
return chain.proceed(requestBuilder.build());
});
})
.build(this);
}
}
2 changes: 1 addition & 1 deletion splunk-otel-android/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,14 @@ dependencies {

implementation("io.opentelemetry:opentelemetry-sdk")
implementation("io.opentelemetry:opentelemetry-exporter-zipkin")
implementation("io.zipkin.reporter2:zipkin-sender-okhttp3")
implementation("io.opentelemetry:opentelemetry-exporter-logging")

implementation("io.opentelemetry.semconv:opentelemetry-semconv:$otelSemconvVersion")
implementation("io.opentelemetry.instrumentation:opentelemetry-okhttp-3.0")

api("io.opentelemetry:opentelemetry-api")
api("com.squareup.okhttp3:okhttp:4.12.0")
api("io.zipkin.reporter2:zipkin-sender-okhttp3:2.17.2")

testImplementation("org.mockito:mockito-core:5.10.0")
testImplementation("org.mockito:mockito-junit-jupiter:5.10.0")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,8 @@ private SpanExporter buildExporter(

private SpanExporter buildStorageBufferingExporter(
CurrentNetworkProvider currentNetworkProvider, SpanStorage spanStorage) {
Sender sender = OkHttpSender.newBuilder().endpoint(getEndpoint()).build();
Sender sender = buildCustomizedZipkinSender();

BandwidthTracker bandwidthTracker = new BandwidthTracker();

FileSender fileSender =
Expand All @@ -359,6 +360,13 @@ private SpanExporter buildStorageBufferingExporter(
return getToDiskExporter(spanStorage);
}

@NonNull
private Sender buildCustomizedZipkinSender() {
OkHttpSender.Builder okBuilder = OkHttpSender.newBuilder().endpoint(getEndpoint());
builder.httpSenderCustomizer.customize(okBuilder);
return okBuilder.build();
}

@NonNull
private String getEndpoint() {
return builder.beaconEndpoint + "?auth=" + builder.rumAccessToken;
Expand Down Expand Up @@ -388,13 +396,15 @@ SpanExporter getToDiskExporter(SpanStorage spanStorage) {
SpanExporter getCoreSpanExporter(String endpoint) {
// return a lazy init exporter so the main thread doesn't block on the setup.
return new LazyInitSpanExporter(
() ->
ZipkinSpanExporter.builder()
.setEncoder(new CustomZipkinEncoder())
.setEndpoint(endpoint)
// remove the local IP address
.setLocalIpAddressSupplier(() -> null)
.build());
() -> {
return ZipkinSpanExporter.builder()
.setEncoder(new CustomZipkinEncoder())
.setEndpoint(endpoint)
// remove the local IP address
.setLocalIpAddressSupplier(() -> null)
.setSender(buildCustomizedZipkinSender())
.build();
});
}

private static class LazyInitSpanExporter implements SpanExporter {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import android.app.Application;
import android.util.Log;
import androidx.annotation.Nullable;
import com.splunk.rum.incubating.HttpSenderCustomizer;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.sdk.trace.export.SpanExporter;
import java.time.Duration;
Expand All @@ -42,6 +43,7 @@ public final class SplunkRumBuilder {
Duration slowRenderingDetectionPollInterval = DEFAULT_SLOW_RENDERING_DETECTION_POLL_INTERVAL;
Attributes globalAttributes = Attributes.empty();
@Nullable String deploymentEnvironment;
HttpSenderCustomizer httpSenderCustomizer = HttpSenderCustomizer.DEFAULT;
private Consumer<SpanFilterBuilder> spanFilterConfigurer = x -> {};
int maxUsageMegabytes = DEFAULT_MAX_STORAGE_USE_MB;
boolean sessionBasedSamplerEnabled = false;
Expand Down Expand Up @@ -78,6 +80,23 @@ public SplunkRumBuilder setBeaconEndpoint(String beaconEndpoint) {
return this;
}

/**
* This method can be used to provide a customizer that will have access to the
* OkHttpSender.Builder before the sender is created. Typical use cases for this are to provide
* custom headers or to modify compression settings. This is a pretty large hammer and should be
* used with caution.
*
* <p>This API is considered incubating and is subject to change.
*
* @param customizer that can make changes to the OkHttpSender.Builder
* @return {@code this}
* @since 1.4.0
*/
public SplunkRumBuilder setHttpSenderCustomizer(HttpSenderCustomizer customizer) {
this.httpSenderCustomizer = customizer;
return this;
}

/**
* Sets the realm for the beacon to send RUM telemetry to. This should be used in place of the
* {@link #setBeaconEndpoint(String)} method in most cases.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* 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 zipkin2.reporter.okhttp3.OkHttpSender;

/**
* This interface can be used to customize the exporter used to send telemetry to Splunk. It is not
* yet stable and its APIs are subject to change at any time.
*
* @since 1.4.0
*/
public interface HttpSenderCustomizer {

HttpSenderCustomizer DEFAULT = x -> {};

void customize(OkHttpSender.Builder builder);
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,15 @@
import static java.util.concurrent.TimeUnit.MINUTES;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import android.app.Application;
import android.content.Context;
import android.os.Looper;
import com.splunk.rum.incubating.HttpSenderCustomizer;
import io.opentelemetry.android.instrumentation.activity.VisibleScreenTracker;
import io.opentelemetry.android.instrumentation.network.CurrentNetwork;
import io.opentelemetry.android.instrumentation.network.CurrentNetworkProvider;
Expand All @@ -48,10 +50,12 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import zipkin2.reporter.okhttp3.OkHttpSender;

@ExtendWith(MockitoExtension.class)
class RumInitializerTest {
Expand Down Expand Up @@ -214,6 +218,30 @@ private TestSpanData createTestSpan(long startTimeNanos) {
.build();
}

@Test
void canCustomizeHttpSender() {
AtomicReference<OkHttpSender.Builder> seenBuilder = new AtomicReference<>();
HttpSenderCustomizer customizer = seenBuilder::set;
SplunkRumBuilder splunkRumBuilder =
new SplunkRumBuilder()
.setRealm("us0")
.setRumAccessToken("secret!")
.setApplicationName("test")
.disableAnrDetection()
.setHttpSenderCustomizer(customizer);

when(application.getApplicationContext()).thenReturn(context);

RumInitializer testInitializer =
new RumInitializer(splunkRumBuilder, application, new AppStartupTimer());
SplunkRum rum = testInitializer.initialize(mainLooper);
rum.addRumEvent("foo", Attributes.empty()); // need to trigger export
rum.flushSpans();

assertNotNull(rum);
assertNotNull(seenBuilder.get());
}

@Test
void shouldTranslateExceptionEventsToSpanAttributes() {
InMemorySpanExporter spanExporter = InMemorySpanExporter.create();
Expand Down

0 comments on commit 47ab538

Please sign in to comment.