Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add basic proxy configuration to OtlpHttp{Signal}Exporters #6270

Merged
merged 10 commits into from
Mar 7, 2024
1 change: 1 addition & 0 deletions dependencyManagement/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ val DEPENDENCIES = listOf(
"org.codehaus.mojo:animal-sniffer-annotations:1.23",
"org.jctools:jctools-core:4.0.3",
"org.junit-pioneer:junit-pioneer:1.9.1",
"org.mock-server:mockserver-netty:5.15.0:shaded",
"org.skyscreamer:jsonassert:1.5.1",
"com.android.tools:desugar_jdk_libs:2.0.4",
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
Comparing source compatibility of against
*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporterBuilder (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporterBuilder setProxyOptions(io.opentelemetry.sdk.common.export.ProxyOptions)
*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporterBuilder (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporterBuilder setProxyOptions(io.opentelemetry.sdk.common.export.ProxyOptions)
*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporterBuilder (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporterBuilder setProxy(io.opentelemetry.sdk.common.export.ProxyOptions)
*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporterBuilder (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporterBuilder setConnectTimeout(long, java.util.concurrent.TimeUnit)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
Comparing source compatibility of against
No changes.
+++ NEW CLASS: PUBLIC(+) FINAL(+) io.opentelemetry.sdk.common.export.ProxyOptions (not serializable)
+++ CLASS FILE FORMAT VERSION: 52.0 <- n.a.
+++ NEW SUPERCLASS: java.lang.Object
+++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.common.export.ProxyOptions create(java.net.ProxySelector)
+++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.common.export.ProxyOptions create(java.net.InetSocketAddress)
+++ NEW METHOD: PUBLIC(+) java.net.ProxySelector getProxySelector()
+++ NEW METHOD: PUBLIC(+) java.lang.String toString()
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import io.opentelemetry.exporter.internal.auth.Authenticator;
import io.opentelemetry.exporter.internal.compression.Compressor;
import io.opentelemetry.exporter.internal.marshal.Marshaler;
import io.opentelemetry.sdk.common.export.ProxyOptions;
import io.opentelemetry.sdk.common.export.RetryPolicy;
import java.net.URI;
import java.util.ArrayList;
Expand Down Expand Up @@ -52,6 +53,7 @@ public final class HttpExporterBuilder<T extends Marshaler> {
private long timeoutNanos = TimeUnit.SECONDS.toNanos(DEFAULT_TIMEOUT_SECS);
@Nullable private Compressor compressor;
private long connectTimeoutNanos = TimeUnit.SECONDS.toNanos(DEFAULT_CONNECT_TIMEOUT_SECS);
@Nullable private ProxyOptions proxyOptions;
private boolean exportAsJson = false;
private final Map<String, String> constantHeaders = new HashMap<>();
private Supplier<Map<String, String>> headerSupplier = Collections::emptyMap;
Expand Down Expand Up @@ -131,6 +133,11 @@ public HttpExporterBuilder<T> setRetryPolicy(RetryPolicy retryPolicy) {
return this;
}

public HttpExporterBuilder<T> setProxyOptions(ProxyOptions proxyOptions) {
this.proxyOptions = proxyOptions;
return this;
}

public HttpExporterBuilder<T> exportAsJson() {
this.exportAsJson = true;
return this;
Expand All @@ -152,6 +159,7 @@ public HttpExporterBuilder<T> copy() {
}
copy.meterProviderSupplier = meterProviderSupplier;
copy.authenticator = authenticator;
copy.proxyOptions = proxyOptions;
return copy;
}

Expand Down Expand Up @@ -187,6 +195,7 @@ public HttpExporter<T> build() {
timeoutNanos,
connectTimeoutNanos,
headerSupplier,
proxyOptions,
authenticator,
retryPolicy,
tlsConfigHelper.getSslContext(),
Expand All @@ -205,6 +214,7 @@ public String toString(boolean includePrefixAndSuffix) {
joiner.add("type=" + type);
joiner.add("endpoint=" + endpoint);
joiner.add("timeoutNanos=" + timeoutNanos);
joiner.add("proxyOptions=" + proxyOptions);
joiner.add(
"compressorEncoding="
+ Optional.ofNullable(compressor).map(Compressor::getEncoding).orElse(null));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import io.opentelemetry.exporter.internal.auth.Authenticator;
import io.opentelemetry.exporter.internal.compression.Compressor;
import io.opentelemetry.sdk.common.export.ProxyOptions;
import io.opentelemetry.sdk.common.export.RetryPolicy;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -34,6 +35,7 @@ HttpSender createSender(
long timeoutNanos,
long connectTimeout,
Supplier<Map<String, List<String>>> headerSupplier,
@Nullable ProxyOptions proxyOptions,
@Nullable Authenticator authenticator,
@Nullable RetryPolicy retryPolicy,
@Nullable SSLContext sslContext,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import io.opentelemetry.exporter.internal.http.HttpExporterBuilder;
import io.opentelemetry.exporter.internal.otlp.logs.LogsRequestMarshaler;
import io.opentelemetry.exporter.otlp.internal.OtlpUserAgent;
import io.opentelemetry.sdk.common.export.ProxyOptions;
import io.opentelemetry.sdk.common.export.RetryPolicy;
import java.time.Duration;
import java.util.Map;
Expand Down Expand Up @@ -171,6 +172,13 @@ public OtlpHttpLogRecordExporterBuilder setRetryPolicy(RetryPolicy retryPolicy)
return this;
}

/** Sets the proxy options. Proxying is disabled by default. */
public OtlpHttpLogRecordExporterBuilder setProxyOptions(ProxyOptions proxyOptions) {
requireNonNull(proxyOptions, "proxyOptions");
delegate.setProxyOptions(proxyOptions);
return this;
}

/**
* Sets the {@link MeterProvider} to use to collect metrics related to export. If not set, uses
* {@link GlobalOpenTelemetry#getMeterProvider()}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import io.opentelemetry.exporter.internal.http.HttpExporterBuilder;
import io.opentelemetry.exporter.internal.otlp.metrics.MetricsRequestMarshaler;
import io.opentelemetry.exporter.otlp.internal.OtlpUserAgent;
import io.opentelemetry.sdk.common.export.ProxyOptions;
import io.opentelemetry.sdk.common.export.RetryPolicy;
import io.opentelemetry.sdk.metrics.InstrumentType;
import io.opentelemetry.sdk.metrics.export.AggregationTemporalitySelector;
Expand Down Expand Up @@ -215,6 +216,13 @@ public OtlpHttpMetricExporterBuilder setRetryPolicy(RetryPolicy retryPolicy) {
return this;
}

/** Sets the proxy options. Proxying is disabled by default. */
public OtlpHttpMetricExporterBuilder setProxyOptions(ProxyOptions proxyOptions) {
requireNonNull(proxyOptions, "proxyOptions");
delegate.setProxyOptions(proxyOptions);
return this;
}

OtlpHttpMetricExporterBuilder exportAsJson() {
delegate.exportAsJson();
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import io.opentelemetry.exporter.internal.http.HttpExporterBuilder;
import io.opentelemetry.exporter.internal.otlp.traces.TraceRequestMarshaler;
import io.opentelemetry.exporter.otlp.internal.OtlpUserAgent;
import io.opentelemetry.sdk.common.export.ProxyOptions;
import io.opentelemetry.sdk.common.export.RetryPolicy;
import java.time.Duration;
import java.util.Map;
Expand Down Expand Up @@ -172,6 +173,13 @@ public OtlpHttpSpanExporterBuilder setRetryPolicy(RetryPolicy retryPolicy) {
return this;
}

/** Sets the proxy options. Proxying is disabled by default. */
public OtlpHttpSpanExporterBuilder setProxy(ProxyOptions proxyOptions) {
requireNonNull(proxyOptions, "proxyOptions");
delegate.setProxyOptions(proxyOptions);
return this;
}

/**
* Sets the {@link MeterProvider} to use to collect metrics related to export. If not set, uses
* {@link GlobalOpenTelemetry#getMeterProvider()}.
Expand Down
1 change: 1 addition & 0 deletions exporters/otlp/testing-internal/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ dependencies {
implementation("com.linecorp.armeria:armeria-junit5")
implementation("io.github.netmikey.logunit:logunit-jul")
implementation("org.assertj:assertj-core")
implementation("org.mock-server:mockserver-netty")
}

// Skip OWASP dependencyCheck task on test module
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,14 @@
import io.opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest;
import io.opentelemetry.proto.collector.trace.v1.ExportTraceServiceResponse;
import io.opentelemetry.sdk.common.CompletableResultCode;
import io.opentelemetry.sdk.common.export.ProxyOptions;
import io.opentelemetry.sdk.common.export.RetryPolicy;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.lang.reflect.Field;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.security.cert.CertificateEncodingException;
Expand Down Expand Up @@ -84,6 +86,7 @@
import org.junit.jupiter.params.provider.ArgumentsProvider;
import org.junit.jupiter.params.provider.ArgumentsSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.mockserver.integration.ClientAndServer;
import org.slf4j.event.Level;
import org.slf4j.event.LoggingEvent;

Expand Down Expand Up @@ -655,6 +658,39 @@ void nonRetryableError(int code) {
assertThat(attempts).hasValue(1);
}

@Test
void proxy() {
// configure mockserver to proxy to the local OTLP server
InetSocketAddress serverSocketAddress = server.httpSocketAddress();
try (ClientAndServer clientAndServer =
ClientAndServer.startClientAndServer(
serverSocketAddress.getHostName(), serverSocketAddress.getPort())) {
TelemetryExporter<T> exporter =
exporterBuilder()
// Configure exporter with server endpoint, and proxy options to route through
// mockserver proxy
.setEndpoint(server.httpUri() + path)
.setProxyOptions(
ProxyOptions.create(
InetSocketAddress.createUnresolved("localhost", clientAndServer.getPort())))
.build();

try {
List<T> telemetry = Collections.singletonList(generateFakeTelemetry());

assertThat(exporter.export(telemetry).join(10, TimeUnit.SECONDS).isSuccess()).isTrue();
// assert that mock server received request
assertThat(clientAndServer.retrieveRecordedRequests(new org.mockserver.model.HttpRequest()))
.hasSize(1);
// assert that server received telemetry from proxy, and is as expected
List<U> expectedResourceTelemetry = toProto(telemetry);
assertThat(exportedResourceTelemetry).containsExactlyElementsOf(expectedResourceTelemetry);
Comment on lines +681 to +687
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the proxy was ignored completely in the implementation (eg. setProxyOptions() was a noop) would this test still pass? If so, is there a way to validate that the proxy was actually used, not just that the data made it across?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the proxy was ignored completely in the implementation (eg. setProxyOptions() was a noop) would this test still pass?

No the test fails. Line 683 retrieve requests recorded by the proxy and verifies it received the expected request. If the implementation fails to route through the configured proxy, the mockserver proxy will never receive the request and line 683 fails.

} finally {
exporter.shutdown();
}
}
}

@Test
@SuppressWarnings("PreferJavaTimeOverload")
void validConfig() {
Expand Down Expand Up @@ -815,6 +851,7 @@ void stringRepresentation() throws IOException, CertificateEncodingException {
+ "timeoutNanos="
+ TimeUnit.SECONDS.toNanos(10)
+ ", "
+ "proxyOptions=null, "
+ "compressorEncoding=null, "
+ "connectTimeoutNanos="
+ TimeUnit.SECONDS.toNanos(10)
Expand Down Expand Up @@ -855,6 +892,7 @@ void stringRepresentation() throws IOException, CertificateEncodingException {
+ "timeoutNanos="
+ TimeUnit.SECONDS.toNanos(5)
+ ", "
+ "proxyOptions=null, "
+ "compressorEncoding=gzip, "
+ "connectTimeoutNanos="
+ TimeUnit.SECONDS.toNanos(4)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import io.grpc.ManagedChannel;
import io.opentelemetry.exporter.internal.auth.Authenticator;
import io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporterBuilder;
import io.opentelemetry.sdk.common.export.ProxyOptions;
import io.opentelemetry.sdk.common.export.RetryPolicy;
import io.opentelemetry.sdk.logs.data.LogRecordData;
import java.time.Duration;
Expand Down Expand Up @@ -44,7 +45,8 @@ public TelemetryExporterBuilder<LogRecordData> setTimeout(Duration timeout) {

@Override
public TelemetryExporterBuilder<LogRecordData> setConnectTimeout(long timeout, TimeUnit unit) {
throw new UnsupportedOperationException();
builder.setConnectTimeout(timeout, unit);
return this;
}

@Override
Expand Down Expand Up @@ -103,6 +105,11 @@ public TelemetryExporterBuilder<LogRecordData> setRetryPolicy(RetryPolicy retryP
return this;
}

@Override
public TelemetryExporterBuilder<LogRecordData> setProxyOptions(ProxyOptions proxyOptions) {
throw new UnsupportedOperationException("ProxyOptions are not supported for gRPC");
}

@Override
@SuppressWarnings("deprecation") // testing deprecated functionality
public TelemetryExporterBuilder<LogRecordData> setChannel(Object channel) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import io.grpc.ManagedChannel;
import io.opentelemetry.exporter.internal.auth.Authenticator;
import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporterBuilder;
import io.opentelemetry.sdk.common.export.ProxyOptions;
import io.opentelemetry.sdk.common.export.RetryPolicy;
import io.opentelemetry.sdk.metrics.data.MetricData;
import java.time.Duration;
Expand Down Expand Up @@ -44,7 +45,8 @@ public TelemetryExporterBuilder<MetricData> setTimeout(Duration timeout) {

@Override
public TelemetryExporterBuilder<MetricData> setConnectTimeout(long timeout, TimeUnit unit) {
throw new UnsupportedOperationException();
builder.setConnectTimeout(timeout, unit);
return this;
}

@Override
Expand Down Expand Up @@ -103,6 +105,11 @@ public TelemetryExporterBuilder<MetricData> setRetryPolicy(RetryPolicy retryPoli
return this;
}

@Override
public TelemetryExporterBuilder<MetricData> setProxyOptions(ProxyOptions proxyOptions) {
throw new UnsupportedOperationException("ProxyOptions are not supported for gRPC");
}

@Override
@SuppressWarnings("deprecation") // testing deprecated functionality
public TelemetryExporterBuilder<MetricData> setChannel(Object channel) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import io.grpc.ManagedChannel;
import io.opentelemetry.exporter.internal.auth.Authenticator;
import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporterBuilder;
import io.opentelemetry.sdk.common.export.ProxyOptions;
import io.opentelemetry.sdk.common.export.RetryPolicy;
import io.opentelemetry.sdk.trace.data.SpanData;
import java.time.Duration;
Expand Down Expand Up @@ -45,7 +46,8 @@ public TelemetryExporterBuilder<SpanData> setTimeout(Duration timeout) {

@Override
public TelemetryExporterBuilder<SpanData> setConnectTimeout(long timeout, TimeUnit unit) {
throw new UnsupportedOperationException();
builder.setConnectTimeout(timeout, unit);
return this;
}

@Override
Expand Down Expand Up @@ -104,6 +106,11 @@ public TelemetryExporterBuilder<SpanData> setRetryPolicy(RetryPolicy retryPolicy
return this;
}

@Override
public TelemetryExporterBuilder<SpanData> setProxyOptions(ProxyOptions proxyOptions) {
throw new UnsupportedOperationException("ProxyOptions are not supported for gRPC");
}

@Override
@SuppressWarnings("deprecation") // testing deprecated functionality
public TelemetryExporterBuilder<SpanData> setChannel(Object channel) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import io.opentelemetry.exporter.internal.auth.Authenticator;
import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporterBuilder;
import io.opentelemetry.sdk.common.export.ProxyOptions;
import io.opentelemetry.sdk.common.export.RetryPolicy;
import io.opentelemetry.sdk.logs.data.LogRecordData;
import java.time.Duration;
Expand Down Expand Up @@ -106,6 +107,12 @@ public TelemetryExporterBuilder<LogRecordData> setRetryPolicy(RetryPolicy retryP
return this;
}

@Override
public TelemetryExporterBuilder<LogRecordData> setProxyOptions(ProxyOptions proxyOptions) {
builder.setProxyOptions(proxyOptions);
return this;
}

@Override
public TelemetryExporterBuilder<LogRecordData> setChannel(Object channel) {
throw new UnsupportedOperationException("Not implemented");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import io.opentelemetry.exporter.internal.auth.Authenticator;
import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporterBuilder;
import io.opentelemetry.sdk.common.export.ProxyOptions;
import io.opentelemetry.sdk.common.export.RetryPolicy;
import io.opentelemetry.sdk.metrics.data.MetricData;
import java.time.Duration;
Expand Down Expand Up @@ -105,6 +106,12 @@ public TelemetryExporterBuilder<MetricData> setRetryPolicy(RetryPolicy retryPoli
return this;
}

@Override
public TelemetryExporterBuilder<MetricData> setProxyOptions(ProxyOptions proxyOptions) {
builder.setProxyOptions(proxyOptions);
return this;
}

@Override
public TelemetryExporterBuilder<MetricData> setChannel(Object channel) {
throw new UnsupportedOperationException("Not implemented");
Expand Down