Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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() {
Comment thread
jack-berg marked this conversation as resolved.
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