Skip to content

Commit

Permalink
Add experimental JdkHttpSender (#5557)
Browse files Browse the repository at this point in the history
  • Loading branch information
jack-berg authored Jul 6, 2023
1 parent cb4d13d commit aab7159
Show file tree
Hide file tree
Showing 22 changed files with 1,064 additions and 36 deletions.
7 changes: 4 additions & 3 deletions all/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ otelJava.moduleName.set("io.opentelemetry.all")
tasks {
// We don't compile much here, just some API boundary tests. This project is mostly for
// aggregating jacoco reports and it doesn't work if this isn't at least as high as the
// highest supported Java version in any of our projects. All of our projects target
// Java 8.
// highest supported Java version in any of our projects. All of our
// projects target Java 8 except :exporters:http-sender:jdk, which targets
// Java 11
withType(JavaCompile::class) {
options.release.set(8)
options.release.set(11)
}

val testJavaVersion: String? by project
Expand Down
32 changes: 32 additions & 0 deletions exporters/common/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,35 @@ dependencies {
testImplementation("io.grpc:grpc-testing")
testRuntimeOnly("io.grpc:grpc-netty-shaded")
}

val testJavaVersion: String? by project

testing {
suites {
register<JvmTestSuite>("testHttpSenderProvider") {
dependencies {
implementation(project(":exporters:sender:jdk"))
implementation(project(":exporters:sender:okhttp"))
}
targets {
all {
testTask {
enabled = !testJavaVersion.equals("8")
}
}
}
}
}
}

tasks {
check {
dependsOn(testing.suites)
}
}

afterEvaluate {
tasks.named<JavaCompile>("compileTestHttpSenderProviderJava") {
options.release.set(11)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
package io.opentelemetry.exporter.internal.http;

import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.internal.ConfigUtil;
import io.opentelemetry.api.metrics.MeterProvider;
import io.opentelemetry.exporter.internal.ExporterBuilderUtil;
import io.opentelemetry.exporter.internal.TlsConfigHelper;
Expand Down Expand Up @@ -129,29 +130,80 @@ public HttpExporter<T> build() {
Map<String, String> headers = this.headers == null ? Collections.emptyMap() : this.headers;
Supplier<Map<String, String>> headerSupplier = () -> headers;

HttpSender httpSender = null;
// TODO: once we publish multiple HttpSenderProviders, log warning when multiple are found
for (HttpSenderProvider httpSenderProvider :
HttpSenderProvider httpSenderProvider = resolveHttpSenderProvider();
HttpSender httpSender =
httpSenderProvider.createSender(
endpoint,
compressionEnabled,
exportAsJson ? "application/json" : "application/x-protobuf",
timeoutNanos,
headerSupplier,
authenticator,
retryPolicy,
tlsConfigHelper.getSslContext(),
tlsConfigHelper.getTrustManager());
LOGGER.log(Level.FINE, "Using HttpSender: " + httpSender.getClass().getName());

return new HttpExporter<>(exporterName, type, httpSender, meterProviderSupplier, exportAsJson);
}

/**
* Resolve the {@link HttpSenderProvider}.
*
* <p>If no {@link HttpSenderProvider} is available, throw {@link IllegalStateException}.
*
* <p>If only one {@link HttpSenderProvider} is available, use it.
*
* <p>If multiple are available and..
*
* <ul>
* <li>{@code io.opentelemetry.exporter.internal.http.HttpSenderProvider} is empty, use the
* first found.
* <li>{@code io.opentelemetry.exporter.internal.http.HttpSenderProvider} is set, use the
* matching provider. If none match, throw {@link IllegalStateException}.
* </ul>
*/
private static HttpSenderProvider resolveHttpSenderProvider() {
Map<String, HttpSenderProvider> httpSenderProviders = new HashMap<>();
for (HttpSenderProvider spi :
ServiceLoader.load(HttpSenderProvider.class, HttpExporterBuilder.class.getClassLoader())) {
httpSender =
httpSenderProvider.createSender(
endpoint,
compressionEnabled,
exportAsJson ? "application/json" : "application/x-protobuf",
timeoutNanos,
headerSupplier,
authenticator,
retryPolicy,
tlsConfigHelper.getSslContext(),
tlsConfigHelper.getTrustManager());
LOGGER.log(Level.FINE, "Using HttpSender: " + httpSender.getClass().getName());
break;
httpSenderProviders.put(spi.getClass().getName(), spi);
}
if (httpSender == null) {

// No provider on classpath, throw
if (httpSenderProviders.isEmpty()) {
throw new IllegalStateException(
"No HttpSenderProvider found on classpath. Please add dependency on opentelemetry-exporter-sender-okhttp");
"No HttpSenderProvider found on classpath. Please add dependency on "
+ "opentelemetry-exporter-sender-okhttp or opentelemetry-exporter-sender-jdk");
}

return new HttpExporter<>(exporterName, type, httpSender, meterProviderSupplier, exportAsJson);
// Exactly one provider on classpath, use it
if (httpSenderProviders.size() == 1) {
return httpSenderProviders.values().stream().findFirst().get();
}

// If we've reached here, there are multiple HttpSenderProviders
String configuredSender =
ConfigUtil.getString("io.opentelemetry.exporter.internal.http.HttpSenderProvider", "");

// Multiple providers but none configured, use first we find and log a warning
if (configuredSender.isEmpty()) {
LOGGER.log(
Level.WARNING,
"Multiple HttpSenderProvider found. Please include only one, "
+ "or specify preference setting io.opentelemetry.exporter.internal.http.HttpSenderProvider "
+ "to the FQCN of the preferred provider.");
return httpSenderProviders.values().stream().findFirst().get();
}

// Multiple providers with configuration match, use configuration match
if (httpSenderProviders.containsKey(configuredSender)) {
return httpSenderProviders.get(configuredSender);
}

// Multiple providers, configured does not match, throw
throw new IllegalStateException(
"No HttpSenderProvider matched configured io.opentelemetry.exporter.internal.http.HttpSenderProvider: "
+ configuredSender);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ void build_NoHttpSenderProvider() {
assertThatThrownBy(() -> new HttpExporterBuilder<>("name", "type", "http://localhost").build())
.isInstanceOf(IllegalStateException.class)
.hasMessage(
"No HttpSenderProvider found on classpath. Please add dependency on opentelemetry-exporter-sender-okhttp");
"No HttpSenderProvider found on classpath. Please add dependency on "
+ "opentelemetry-exporter-sender-okhttp or opentelemetry-exporter-sender-jdk");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.exporter.internal.http;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

import io.github.netmikey.logunit.api.LogCapturer;
import io.opentelemetry.exporter.sender.jdk.internal.JdkHttpSender;
import io.opentelemetry.exporter.sender.okhttp.internal.OkHttpHttpSender;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junitpioneer.jupiter.SetSystemProperty;

class HttpExporterTest {

@RegisterExtension
LogCapturer logCapturer =
LogCapturer.create().captureForLogger(HttpExporterBuilder.class.getName());

@Test
void build_multipleSendersNoConfiguration() {
Assertions.assertThatCode(
() -> new HttpExporterBuilder<>("exporter", "type", "http://localhost").build())
.doesNotThrowAnyException();

logCapturer.assertContains(
"Multiple HttpSenderProvider found. Please include only one, "
+ "or specify preference setting io.opentelemetry.exporter.internal.http.HttpSenderProvider "
+ "to the FQCN of the preferred provider.");
}

@Test
@SetSystemProperty(
key = "io.opentelemetry.exporter.internal.http.HttpSenderProvider",
value = "io.opentelemetry.exporter.sender.jdk.internal.JdkHttpSenderProvider")
void build_multipleSendersWithJdk() {
assertThat(new HttpExporterBuilder<>("exporter", "type", "http://localhost").build())
.extracting("httpSender")
.isInstanceOf(JdkHttpSender.class);

assertThat(logCapturer.getEvents()).isEmpty();
}

@Test
@SetSystemProperty(
key = "io.opentelemetry.exporter.internal.http.HttpSenderProvider",
value = "io.opentelemetry.exporter.sender.okhttp.internal.OkHttpHttpSenderProvider")
void build_multipleSendersWithOkHttp() {
assertThat(new HttpExporterBuilder<>("exporter", "type", "http://localhost").build())
.extracting("httpSender")
.isInstanceOf(OkHttpHttpSender.class);

assertThat(logCapturer.getEvents()).isEmpty();
}

@Test
@SetSystemProperty(
key = "io.opentelemetry.exporter.internal.http.HttpSenderProvider",
value = "foo")
void build_multipleSendersNoMatch() {
assertThatThrownBy(
() -> new HttpExporterBuilder<>("exporter", "type", "http://localhost").build())
.isInstanceOf(IllegalStateException.class)
.hasMessage(
"No HttpSenderProvider matched configured io.opentelemetry.exporter.internal.http.HttpSenderProvider: foo");

assertThat(logCapturer.getEvents()).isEmpty();
}
}
24 changes: 24 additions & 0 deletions exporters/otlp/all/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ dependencies {
jmhRuntimeOnly("io.grpc:grpc-netty")
}

val testJavaVersion: String? by project

testing {
suites {
register<JvmTestSuite>("testGrpcNetty") {
Expand All @@ -65,6 +67,22 @@ testing {
implementation("io.grpc:grpc-stub")
}
}
register<JvmTestSuite>("testJdkHttpSender") {
dependencies {
implementation(project(":exporters:sender:jdk"))
implementation(project(":exporters:otlp:testing-internal"))

implementation("io.grpc:grpc-stub")
}
targets {
all {
testTask {
systemProperty("io.opentelemetry.exporter.internal.http.HttpSenderProvider", "io.opentelemetry.exporter.sender.jdk.internal.JdkHttpSenderProvider")
enabled = !testJavaVersion.equals("8")
}
}
}
}
register<JvmTestSuite>("testSpanPipeline") {
dependencies {
implementation("io.opentelemetry.proto:opentelemetry-proto")
Expand All @@ -86,3 +104,9 @@ tasks {
)
}
}

afterEvaluate {
tasks.named<JavaCompile>("compileTestJdkHttpSenderJava") {
options.release.set(11)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@
import javax.net.ssl.SSLContext;
import javax.net.ssl.X509TrustManager;

class OtlpHttpLogRecordExporterTest
class OtlpHttpLogRecordExporterOkHttpSenderTest
extends AbstractHttpTelemetryExporterTest<LogRecordData, ResourceLogs> {

protected OtlpHttpLogRecordExporterTest() {
protected OtlpHttpLogRecordExporterOkHttpSenderTest() {
super("log", "/v1/logs", ResourceLogs.getDefaultInstance());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@
import javax.net.ssl.X509TrustManager;
import org.junit.jupiter.api.Test;

class OtlpHttpMetricExporterTest
class OtlpHttpMetricExporterOkHttpSenderTest
extends AbstractHttpTelemetryExporterTest<MetricData, ResourceMetrics> {

protected OtlpHttpMetricExporterTest() {
protected OtlpHttpMetricExporterOkHttpSenderTest() {
super("metric", "/v1/metrics", ResourceMetrics.getDefaultInstance());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@
import javax.net.ssl.SSLContext;
import javax.net.ssl.X509TrustManager;

class OtlpHttpSpanExporterTest extends AbstractHttpTelemetryExporterTest<SpanData, ResourceSpans> {
class OtlpHttpSpanExporterOkHttpSenderTest
extends AbstractHttpTelemetryExporterTest<SpanData, ResourceSpans> {

protected OtlpHttpSpanExporterTest() {
protected OtlpHttpSpanExporterOkHttpSenderTest() {
super("span", "/v1/traces", ResourceSpans.getDefaultInstance());
}

Expand Down
Loading

0 comments on commit aab7159

Please sign in to comment.