diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-exporter-http-sender-okhttp.txt b/docs/apidiffs/current_vs_latest/opentelemetry-exporter-http-sender-okhttp.txt new file mode 100644 index 00000000000..df26146497b --- /dev/null +++ b/docs/apidiffs/current_vs_latest/opentelemetry-exporter-http-sender-okhttp.txt @@ -0,0 +1,2 @@ +Comparing source compatibility of against +No changes. \ No newline at end of file diff --git a/exporters/common/build.gradle.kts b/exporters/common/build.gradle.kts index cab0b2e2527..0db9a6cd977 100644 --- a/exporters/common/build.gradle.kts +++ b/exporters/common/build.gradle.kts @@ -18,7 +18,7 @@ dependencies { annotationProcessor("com.google.auto.value:auto-value") - // We include helpers shared by gRPC or okhttp exporters but do not want to impose these + // We include helpers shared by gRPC exporters but do not want to impose these // dependency on all of our consumers. compileOnly("com.fasterxml.jackson.core:jackson-core") compileOnly("com.squareup.okhttp3:okhttp") diff --git a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/http/HttpExporterBuilder.java b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/http/HttpExporterBuilder.java index db65b6336a6..788831ab93b 100644 --- a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/http/HttpExporterBuilder.java +++ b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/http/HttpExporterBuilder.java @@ -11,15 +11,17 @@ import io.opentelemetry.exporter.internal.TlsConfigHelper; import io.opentelemetry.exporter.internal.auth.Authenticator; import io.opentelemetry.exporter.internal.marshal.Marshaler; -import io.opentelemetry.exporter.internal.okhttp.OkHttpHttpSender; import io.opentelemetry.exporter.internal.retry.RetryPolicy; import java.net.URI; import java.time.Duration; import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.ServiceLoader; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.annotation.Nullable; import javax.net.ssl.SSLContext; import javax.net.ssl.X509TrustManager; @@ -34,6 +36,8 @@ public final class HttpExporterBuilder { public static final long DEFAULT_TIMEOUT_SECS = 10; + private static final Logger LOGGER = Logger.getLogger(HttpExporterBuilder.class.getName()); + private final String exporterName; private final String type; @@ -125,17 +129,28 @@ public HttpExporter build() { Map headers = this.headers == null ? Collections.emptyMap() : this.headers; Supplier> headerSupplier = () -> headers; - HttpSender httpSender = - new OkHttpHttpSender( - endpoint, - compressionEnabled, - exportAsJson ? "application/json" : "application/x-protobuf", - timeoutNanos, - headerSupplier, - authenticator, - retryPolicy, - tlsConfigHelper.getSslContext(), - tlsConfigHelper.getTrustManager()); + HttpSender httpSender = null; + // TODO: once we publish multiple HttpSenderProviders, log warning when multiple are found + for (HttpSenderProvider httpSenderProvider : + 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; + } + if (httpSender == null) { + throw new IllegalStateException( + "No HttpSenderProvider found on classpath. Please add dependency on opentelemetry-exporter-http-sender-okhttp"); + } return new HttpExporter<>(exporterName, type, httpSender, meterProviderSupplier, exportAsJson); } diff --git a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/http/HttpSenderProvider.java b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/http/HttpSenderProvider.java new file mode 100644 index 00000000000..42197565d49 --- /dev/null +++ b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/http/HttpSenderProvider.java @@ -0,0 +1,36 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.internal.http; + +import io.opentelemetry.exporter.internal.auth.Authenticator; +import io.opentelemetry.exporter.internal.retry.RetryPolicy; +import java.util.Map; +import java.util.function.Supplier; +import javax.annotation.Nullable; +import javax.net.ssl.SSLContext; +import javax.net.ssl.X509TrustManager; + +/** + * A service provider interface (SPI) for providing {@link HttpSender}s backed by different HTTP + * client libraries. + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ +public interface HttpSenderProvider { + + /** Returns a {@link HttpSender} configured with the provided parameters. */ + HttpSender createSender( + String endpoint, + boolean compressionEnabled, + String contentType, + long timeoutNanos, + Supplier> headerSupplier, + @Nullable Authenticator authenticator, + @Nullable RetryPolicy retryPolicy, + @Nullable SSLContext sslContext, + @Nullable X509TrustManager trustManager); +} diff --git a/exporters/common/src/test/java/io/opentelemetry/exporter/internal/http/HttpExporterTest.java b/exporters/common/src/test/java/io/opentelemetry/exporter/internal/http/HttpExporterTest.java new file mode 100644 index 00000000000..d99e58dfcfe --- /dev/null +++ b/exporters/common/src/test/java/io/opentelemetry/exporter/internal/http/HttpExporterTest.java @@ -0,0 +1,21 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.internal.http; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.junit.jupiter.api.Test; + +class HttpExporterTest { + + @Test + 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-http-sender-okhttp"); + } +} diff --git a/exporters/http-sender/build.gradle.kts b/exporters/http-sender/build.gradle.kts new file mode 100644 index 00000000000..463f46b15cd --- /dev/null +++ b/exporters/http-sender/build.gradle.kts @@ -0,0 +1,10 @@ +subprojects { + // Workaround https://github.com/gradle/gradle/issues/847 + group = "io.opentelemetry.exporter.httpsender" + val proj = this + plugins.withId("java") { + configure { + archivesName.set("opentelemetry-exporter-http-sender-${proj.name}") + } + } +} diff --git a/exporters/http-sender/okhttp/build.gradle.kts b/exporters/http-sender/okhttp/build.gradle.kts new file mode 100644 index 00000000000..d34a4e11d49 --- /dev/null +++ b/exporters/http-sender/okhttp/build.gradle.kts @@ -0,0 +1,18 @@ +plugins { + id("otel.java-conventions") + id("otel.publish-conventions") + + id("otel.animalsniffer-conventions") +} + +description = "OpenTelemetry OkHttp HttpSender" +otelJava.moduleName.set("io.opentelemetry.exporter.http.okhttp.internal") + +dependencies { + implementation(project(":exporters:common")) + implementation(project(":sdk:common")) + + implementation("com.squareup.okhttp3:okhttp") + + testImplementation("com.linecorp.armeria:armeria-junit5") +} diff --git a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/okhttp/OkHttpHttpSender.java b/exporters/http-sender/okhttp/src/main/java/io/opentelemetry/exporter/http/okhttp/internal/OkHttpHttpSender.java similarity index 98% rename from exporters/common/src/main/java/io/opentelemetry/exporter/internal/okhttp/OkHttpHttpSender.java rename to exporters/http-sender/okhttp/src/main/java/io/opentelemetry/exporter/http/okhttp/internal/OkHttpHttpSender.java index 442f0f8efa7..c966640306f 100644 --- a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/okhttp/OkHttpHttpSender.java +++ b/exporters/http-sender/okhttp/src/main/java/io/opentelemetry/exporter/http/okhttp/internal/OkHttpHttpSender.java @@ -3,10 +3,11 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.exporter.internal.okhttp; +package io.opentelemetry.exporter.http.okhttp.internal; import io.opentelemetry.exporter.internal.auth.Authenticator; import io.opentelemetry.exporter.internal.http.HttpSender; +import io.opentelemetry.exporter.internal.okhttp.OkHttpUtil; import io.opentelemetry.exporter.internal.retry.RetryInterceptor; import io.opentelemetry.exporter.internal.retry.RetryPolicy; import io.opentelemetry.exporter.internal.retry.RetryUtil; diff --git a/exporters/http-sender/okhttp/src/main/java/io/opentelemetry/exporter/http/okhttp/internal/OkHttpHttpSenderProvider.java b/exporters/http-sender/okhttp/src/main/java/io/opentelemetry/exporter/http/okhttp/internal/OkHttpHttpSenderProvider.java new file mode 100644 index 00000000000..64398368e79 --- /dev/null +++ b/exporters/http-sender/okhttp/src/main/java/io/opentelemetry/exporter/http/okhttp/internal/OkHttpHttpSenderProvider.java @@ -0,0 +1,48 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.http.okhttp.internal; + +import io.opentelemetry.exporter.internal.auth.Authenticator; +import io.opentelemetry.exporter.internal.http.HttpSender; +import io.opentelemetry.exporter.internal.http.HttpSenderProvider; +import io.opentelemetry.exporter.internal.retry.RetryPolicy; +import java.util.Map; +import java.util.function.Supplier; +import javax.net.ssl.SSLContext; +import javax.net.ssl.X509TrustManager; +import org.jetbrains.annotations.Nullable; + +/** + * {@link HttpSender} SPI implementation for {@link OkHttpHttpSender}. + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ +public class OkHttpHttpSenderProvider implements HttpSenderProvider { + + @Override + public HttpSender createSender( + String endpoint, + boolean compressionEnabled, + String contentType, + long timeoutNanos, + Supplier> headerSupplier, + @Nullable Authenticator authenticator, + @Nullable RetryPolicy retryPolicy, + @Nullable SSLContext sslContext, + @Nullable X509TrustManager trustManager) { + return new OkHttpHttpSender( + endpoint, + compressionEnabled, + contentType, + timeoutNanos, + headerSupplier, + authenticator, + retryPolicy, + sslContext, + trustManager); + } +} diff --git a/exporters/http-sender/okhttp/src/main/java/io/opentelemetry/exporter/http/okhttp/internal/package-info.java b/exporters/http-sender/okhttp/src/main/java/io/opentelemetry/exporter/http/okhttp/internal/package-info.java new file mode 100644 index 00000000000..42c2d178acc --- /dev/null +++ b/exporters/http-sender/okhttp/src/main/java/io/opentelemetry/exporter/http/okhttp/internal/package-info.java @@ -0,0 +1,10 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +/** Utilities for HTTP exporters. */ +@ParametersAreNonnullByDefault +package io.opentelemetry.exporter.http.okhttp.internal; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/exporters/http-sender/okhttp/src/main/resources/META-INF/services/io.opentelemetry.exporter.internal.http.HttpSenderProvider b/exporters/http-sender/okhttp/src/main/resources/META-INF/services/io.opentelemetry.exporter.internal.http.HttpSenderProvider new file mode 100644 index 00000000000..8f8054934e9 --- /dev/null +++ b/exporters/http-sender/okhttp/src/main/resources/META-INF/services/io.opentelemetry.exporter.internal.http.HttpSenderProvider @@ -0,0 +1 @@ +io.opentelemetry.exporter.http.okhttp.internal.OkHttpHttpSenderProvider diff --git a/exporters/common/src/test/java/io/opentelemetry/exporter/internal/auth/AuthenticatingExporterTest.java b/exporters/http-sender/okhttp/src/test/java/io/opentelemetry/exporter/http/okhttp/internal/AuthenticatingExporterTest.java similarity index 98% rename from exporters/common/src/test/java/io/opentelemetry/exporter/internal/auth/AuthenticatingExporterTest.java rename to exporters/http-sender/okhttp/src/test/java/io/opentelemetry/exporter/http/okhttp/internal/AuthenticatingExporterTest.java index 4a430b9bb0e..0e605e476eb 100644 --- a/exporters/common/src/test/java/io/opentelemetry/exporter/internal/auth/AuthenticatingExporterTest.java +++ b/exporters/http-sender/okhttp/src/test/java/io/opentelemetry/exporter/http/okhttp/internal/AuthenticatingExporterTest.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.exporter.internal.auth; +package io.opentelemetry.exporter.http.okhttp.internal; import static org.assertj.core.api.Assertions.assertThat; diff --git a/exporters/common/src/test/java/io/opentelemetry/exporter/internal/okhttp/HttpExporterBuilderTest.java b/exporters/http-sender/okhttp/src/test/java/io/opentelemetry/exporter/http/okhttp/internal/HttpExporterBuilderTest.java similarity index 62% rename from exporters/common/src/test/java/io/opentelemetry/exporter/internal/okhttp/HttpExporterBuilderTest.java rename to exporters/http-sender/okhttp/src/test/java/io/opentelemetry/exporter/http/okhttp/internal/HttpExporterBuilderTest.java index f81db2ae116..0e8bc6884c3 100644 --- a/exporters/common/src/test/java/io/opentelemetry/exporter/internal/okhttp/HttpExporterBuilderTest.java +++ b/exporters/http-sender/okhttp/src/test/java/io/opentelemetry/exporter/http/okhttp/internal/HttpExporterBuilderTest.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.exporter.internal.okhttp; +package io.opentelemetry.exporter.http.okhttp.internal; import static org.assertj.core.api.Assertions.assertThat; @@ -25,7 +25,11 @@ void compressionDefault() { .isInstanceOfSatisfying( HttpExporter.class, otlp -> - assertThat(otlp).extracting("httpSender.compressionEnabled").isEqualTo(false)); + assertThat(otlp) + .extracting("httpSender") + .isInstanceOf(OkHttpHttpSender.class) + .extracting("compressionEnabled") + .isEqualTo(false)); } finally { exporter.shutdown(); } @@ -39,7 +43,11 @@ void compressionNone() { .isInstanceOfSatisfying( HttpExporter.class, otlp -> - assertThat(otlp).extracting("httpSender.compressionEnabled").isEqualTo(false)); + assertThat(otlp) + .extracting("httpSender") + .isInstanceOf(OkHttpHttpSender.class) + .extracting("compressionEnabled") + .isEqualTo(false)); } finally { exporter.shutdown(); } @@ -52,7 +60,12 @@ void compressionGzip() { assertThat(exporter) .isInstanceOfSatisfying( HttpExporter.class, - otlp -> assertThat(otlp).extracting("httpSender.compressionEnabled").isEqualTo(true)); + otlp -> + assertThat(otlp) + .extracting("httpSender") + .isInstanceOf(OkHttpHttpSender.class) + .extracting("compressionEnabled") + .isEqualTo(true)); } finally { exporter.shutdown(); } @@ -67,7 +80,11 @@ void compressionEnabledAndDisabled() { .isInstanceOfSatisfying( HttpExporter.class, otlp -> - assertThat(otlp).extracting("httpSender.compressionEnabled").isEqualTo(false)); + assertThat(otlp) + .extracting("httpSender") + .isInstanceOf(OkHttpHttpSender.class) + .extracting("compressionEnabled") + .isEqualTo(false)); } finally { exporter.shutdown(); } diff --git a/exporters/otlp/all/build.gradle.kts b/exporters/otlp/all/build.gradle.kts index 7585a72b1b3..7a8b37c3d62 100644 --- a/exporters/otlp/all/build.gradle.kts +++ b/exporters/otlp/all/build.gradle.kts @@ -17,6 +17,7 @@ dependencies { api(project(":sdk:logs")) implementation(project(":exporters:otlp:common")) + implementation(project(":exporters:http-sender:okhttp")) implementation(project(":sdk-extensions:autoconfigure-spi")) implementation("com.squareup.okhttp3:okhttp") diff --git a/settings.gradle.kts b/settings.gradle.kts index 2b735925143..504ffb435de 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -32,6 +32,7 @@ include(":extensions:incubator") include(":extensions:kotlin") include(":extensions:trace-propagators") include(":exporters:common") +include(":exporters:http-sender:okhttp") include(":exporters:jaeger") include(":exporters:jaeger-proto") include(":exporters:jaeger-thrift")