Add support for signal specific timeout and headers autoconfiguration. (#3434)

* Add support for signal specific timeout and headers autoconfiguration.

* Attempt to fix build.

Co-authored-by: Anuraag Agrawal <aanuraag@amazon.co.jp>
This commit is contained in:
jack-berg 2021-08-03 12:43:45 -05:00 committed by GitHub
parent 2d9a949c70
commit 42831fe004
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 353 additions and 339 deletions

View File

@ -51,8 +51,12 @@ The [OpenTelemetry Protocol (OTLP)](https://github.com/open-telemetry/openteleme
| otel.exporter.otlp.endpoint | OTEL_EXPORTER_OTLP_ENDPOINT | The OTLP traces and metrics endpoint to connect to. Must be a URL with a scheme of either `http` or `https` based on the use of TLS. Default is `http://localhost:4317`. |
| otel.exporter.otlp.traces.endpoint | OTEL_EXPORTER_OTLP_TRACES_ENDPOINT | The OTLP traces endpoint to connect to. Must be a URL with a scheme of either `http` or `https` based on the use of TLS. Default is `http://localhost:4317`. |
| otel.exporter.otlp.metrics.endpoint | OTEL_EXPORTER_OTLP_METRICS_ENDPOINT | The OTLP metrics endpoint to connect to. Must be a URL with a scheme of either `http` or `https` based on the use of TLS. Default is `http://localhost:4317`. |
| otel.exporter.otlp.headers | OTEL_EXPORTER_OTLP_HEADERS | Key-value pairs separated by commas to pass as request headers. |
| otel.exporter.otlp.timeout | OTEL_EXPORTER_OTLP_TIMEOUT | The maximum waiting time, in milliseconds, allowed to send each batch. Default is `10000`. |
| otel.exporter.otlp.headers | OTEL_EXPORTER_OTLP_HEADERS | Key-value pairs separated by commas to pass as request headers on OTLP trace and metrics requests. |
| otel.exporter.otlp.traces.headers | OTEL_EXPORTER_OTLP_TRACES_HEADERS | Key-value pairs separated by commas to pass as request headers on OTLP trace requests. |
| otel.exporter.otlp.metrics.headers | OTEL_EXPORTER_OTLP_METRICS_HEADERS | Key-value pairs separated by commas to pass as request headers on OTLP metrics requests. |
| otel.exporter.otlp.timeout | OTEL_EXPORTER_OTLP_TIMEOUT | The maximum waiting time, in milliseconds, allowed to send each OTLP trace and metric batch. Default is `10000`. |
| otel.exporter.otlp.traces.timeout | OTEL_EXPORTER_OTLP_TRACES_TIMEOUT | The maximum waiting time, in milliseconds, allowed to send each OTLP trace batch. Default is `10000`. |
| otel.exporter.otlp.metrics.timeout | OTEL_EXPORTER_OTLP_METRICS_TIMEOUT | The maximum waiting time, in milliseconds, allowed to send each OTLP metric batch. Default is `10000`. |
To configure the service name for the OTLP exporter, add the `service.name` key
to the OpenTelemetry Resource ([see below](#opentelemetry-resource)), e.g. `OTEL_RESOURCE_ATTRIBUTES=service.name=myservice`.

View File

@ -14,7 +14,7 @@ testSets {
create("testInitializeRegistersGlobal")
create("testJaeger")
create("testPrometheus")
create("testOtlpTls")
create("testOtlp")
create("testResourceDisabledByProperty")
create("testResourceDisabledByEnv")
create("testZipkin")
@ -54,7 +54,9 @@ dependencies {
add("testFullConfigImplementation", project(":exporters:zipkin"))
add("testFullConfigImplementation", project(":sdk-extensions:resources"))
add("testOtlpTlsImplementation", project(":exporters:otlp:all"))
add("testOtlpImplementation", project(":exporters:otlp:all"))
add("testOtlpImplementation", project(":exporters:otlp:metrics"))
add("testOtlpImplementation", "org.bouncycastle:bcpkix-jdk15on")
add("testJaegerImplementation", project(":exporters:jaeger"))
@ -97,12 +99,7 @@ tasks {
environment("OTEL_BSP_SCHEDULE_DELAY", "10")
}
val testOtlpTls by existing(Test::class) {
environment("OTEL_RESOURCE_ATTRIBUTES", "service.name=test,cat=meow")
environment("OTEL_TRACES_EXPORTER", "otlp")
environment("OTEL_METRICS_EXPORTER", "none")
environment("OTEL_BSP_SCHEDULE_DELAY", "10")
}
val testOtlp by existing(Test::class)
val testZipkin by existing(Test::class) {
environment("OTEL_TRACES_EXPORTER", "zipkin")
@ -131,6 +128,7 @@ tasks {
testConfigError,
testFullConfig,
testJaeger,
testOtlp,
testPrometheus,
testZipkin,
testResourceDisabledByProperty,

View File

@ -187,7 +187,7 @@ public final class ConfigProperties {
* comma-separated for each key, with an '=' separating the key and value. For instance, <code>
* service.name=Greatest Service,host.name=localhost</code> Empty values will be removed.
*
* @return an empty list if the property has not been configured.
* @return an empty map if the property has not been configured.
*/
public Map<String, String> getCommaSeparatedMap(String name) {
return getCommaSeparatedValues(name).stream()

View File

@ -17,6 +17,7 @@ import io.prometheus.client.exporter.HTTPServer;
import java.io.IOException;
import java.time.Duration;
import java.util.Collections;
import java.util.Map;
import javax.annotation.Nullable;
final class MetricExporterConfiguration {
@ -72,9 +73,18 @@ final class MetricExporterConfiguration {
builder.setEndpoint(endpoint);
}
Map<String, String> headers = config.getCommaSeparatedMap("otel.exporter.otlp.metrics.headers");
if (headers.isEmpty()) {
headers = config.getCommaSeparatedMap("otel.exporter.otlp.headers");
}
headers.forEach(builder::addHeader);
config.getCommaSeparatedMap("otel.exporter.otlp.headers").forEach(builder::addHeader);
Duration timeout = config.getDuration("otel.exporter.otlp.timeout");
Duration timeout = config.getDuration("otel.exporter.otlp.metrics.timeout");
if (timeout == null) {
timeout = config.getDuration("otel.exporter.otlp.timeout");
}
if (timeout != null) {
builder.setTimeout(timeout);
}

View File

@ -78,9 +78,16 @@ final class SpanExporterConfiguration {
builder.setEndpoint(endpoint);
}
config.getCommaSeparatedMap("otel.exporter.otlp.headers").forEach(builder::addHeader);
Map<String, String> headers = config.getCommaSeparatedMap("otel.exporter.otlp.traces.headers");
if (headers.isEmpty()) {
headers = config.getCommaSeparatedMap("otel.exporter.otlp.headers");
}
headers.forEach(builder::addHeader);
Duration timeout = config.getDuration("otel.exporter.otlp.timeout");
Duration timeout = config.getDuration("otel.exporter.otlp.traces.timeout");
if (timeout == null) {
timeout = config.getDuration("otel.exporter.otlp.timeout");
}
if (timeout != null) {
builder.setTimeout(timeout);
}

View File

@ -1,191 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.sdk.autoconfigure;
import static io.opentelemetry.api.common.AttributeKey.stringKey;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.assertj.core.api.Assertions.assertThat;
import static org.awaitility.Awaitility.await;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.linecorp.armeria.server.ServerBuilder;
import com.linecorp.armeria.server.grpc.GrpcService;
import com.linecorp.armeria.testing.junit5.server.ServerExtension;
import io.grpc.stub.StreamObserver;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporter;
import io.opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceRequest;
import io.opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceResponse;
import io.opentelemetry.proto.collector.metrics.v1.MetricsServiceGrpc;
import io.opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest;
import io.opentelemetry.proto.collector.trace.v1.ExportTraceServiceResponse;
import io.opentelemetry.proto.collector.trace.v1.TraceServiceGrpc;
import io.opentelemetry.sdk.common.InstrumentationLibraryInfo;
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
import io.opentelemetry.sdk.metrics.data.AggregationTemporality;
import io.opentelemetry.sdk.metrics.data.LongPointData;
import io.opentelemetry.sdk.metrics.data.LongSumData;
import io.opentelemetry.sdk.metrics.data.MetricData;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.testing.trace.TestSpanData;
import io.opentelemetry.sdk.trace.data.StatusData;
import io.opentelemetry.sdk.trace.export.SpanExporter;
import java.util.Collections;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
@SuppressWarnings("InterruptedExceptionSwallowed")
class EndpointConfigurationTest {
private static final BlockingQueue<ExportTraceServiceRequest> otlpTraceRequests =
new LinkedBlockingDeque<>();
private static final BlockingQueue<ExportMetricsServiceRequest> otlpMetricsRequests =
new LinkedBlockingDeque<>();
@RegisterExtension
public static final ServerExtension server =
new ServerExtension() {
@Override
protected void configure(ServerBuilder sb) {
sb.service(
GrpcService.builder()
// OTLP spans
.addService(
new TraceServiceGrpc.TraceServiceImplBase() {
@Override
public void export(
ExportTraceServiceRequest request,
StreamObserver<ExportTraceServiceResponse> responseObserver) {
otlpTraceRequests.add(request);
responseObserver.onNext(ExportTraceServiceResponse.getDefaultInstance());
responseObserver.onCompleted();
}
})
// OTLP metrics
.addService(
new MetricsServiceGrpc.MetricsServiceImplBase() {
@Override
public void export(
ExportMetricsServiceRequest request,
StreamObserver<ExportMetricsServiceResponse> responseObserver) {
if (request.getResourceMetricsCount() > 0) {
otlpMetricsRequests.add(request);
}
responseObserver.onNext(
ExportMetricsServiceResponse.getDefaultInstance());
responseObserver.onCompleted();
}
})
.useBlockingTaskExecutor(true)
.build());
}
};
@BeforeEach
void setUp() {
otlpTraceRequests.clear();
otlpMetricsRequests.clear();
GlobalOpenTelemetry.resetForTest();
}
@AfterEach
public void tearDown() {
GlobalOpenTelemetry.resetForTest();
}
@Test
public void configure() {
SpanExporter spanExporter =
SpanExporterConfiguration.configureExporter(
"otlp",
ConfigProperties.createForTest(
ImmutableMap.of(
"otel.exporter.otlp.traces.endpoint",
"http://localhost:" + server.httpPort())));
OtlpGrpcMetricExporter metricExporter =
MetricExporterConfiguration.configureOtlpMetrics(
ConfigProperties.createForTest(
ImmutableMap.of(
"otel.exporter.otlp.metrics.endpoint",
"http://localhost:" + server.httpPort())),
SdkMeterProvider.builder().build());
spanExporter.export(
Lists.newArrayList(
TestSpanData.builder()
.setHasEnded(true)
.setName("name")
.setStartEpochNanos(MILLISECONDS.toNanos(System.currentTimeMillis()))
.setEndEpochNanos(MILLISECONDS.toNanos(System.currentTimeMillis()))
.setKind(SpanKind.SERVER)
.setStatus(StatusData.error())
.setTotalRecordedEvents(0)
.setTotalRecordedLinks(0)
.build()));
metricExporter.export(
Lists.newArrayList(
MetricData.createLongSum(
Resource.empty(),
InstrumentationLibraryInfo.empty(),
"metric_name",
"metric_description",
"ms",
LongSumData.create(
false,
AggregationTemporality.CUMULATIVE,
Collections.singletonList(
LongPointData.create(
MILLISECONDS.toNanos(System.currentTimeMillis()),
MILLISECONDS.toNanos(System.currentTimeMillis()),
Attributes.of(stringKey("key"), "value"),
10))))));
await()
.untilAsserted(
() -> {
assertThat(otlpTraceRequests).hasSize(1);
assertThat(otlpMetricsRequests).hasSize(1);
});
}
@Test
void configuresGlobal() {
// Point "otel.exporter.otlp.endpoint" to wrong endpoint
System.setProperty("otel.exporter.otlp.endpoint", "http://localhost/wrong");
// Point "otel.exporter.otlp.traces.endpoint" to correct endpoint
System.setProperty(
"otel.exporter.otlp.traces.endpoint", "http://localhost:" + server.httpPort());
// Point "otel.exporter.otlp.metrics.endpoint" to correct endpoint
System.setProperty(
"otel.exporter.otlp.metrics.endpoint", "http://localhost:" + server.httpPort());
System.setProperty("otel.exporter.otlp.timeout", "10000");
GlobalOpenTelemetry.get().getTracer("test").spanBuilder("test").startSpan().end();
await()
.untilAsserted(
() -> {
assertThat(otlpTraceRequests).hasSize(1);
// Not well defined how many metric exports would have happened by now, check that
// any
// did. The metrics will be BatchSpanProcessor metrics.
assertThat(otlpMetricsRequests).isNotEmpty();
});
}
}

View File

@ -1,39 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.sdk.autoconfigure;
import static org.assertj.core.api.Assertions.assertThat;
import com.google.common.collect.ImmutableMap;
import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporter;
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.Test;
class MetricExporterConfigurationTest {
// Timeout difficult to test using real exports so just check implementation detail here.
@Test
void configureOtlpTimeout() {
OtlpGrpcMetricExporter exporter =
MetricExporterConfiguration.configureOtlpMetrics(
ConfigProperties.createForTest(
ImmutableMap.of(
"otel.exporter.otlp.timeout", "10ms",
"otel.imr.export.interval", "5s")),
SdkMeterProvider.builder().build());
try {
assertThat(exporter)
.isInstanceOfSatisfying(
OtlpGrpcMetricExporter.class,
otlp ->
assertThat(otlp)
.extracting("timeoutNanos")
.isEqualTo(TimeUnit.MILLISECONDS.toNanos(10L)));
} finally {
exporter.shutdown();
}
}
}

View File

@ -0,0 +1,310 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.sdk.autoconfigure;
import static io.opentelemetry.api.common.AttributeKey.stringKey;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.awaitility.Awaitility.await;
import com.google.common.collect.Lists;
import com.linecorp.armeria.common.RequestHeaders;
import com.linecorp.armeria.server.ServerBuilder;
import com.linecorp.armeria.server.grpc.GrpcService;
import com.linecorp.armeria.testing.junit5.server.SelfSignedCertificateExtension;
import com.linecorp.armeria.testing.junit5.server.ServerExtension;
import io.grpc.stub.StreamObserver;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceRequest;
import io.opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceResponse;
import io.opentelemetry.proto.collector.metrics.v1.MetricsServiceGrpc;
import io.opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest;
import io.opentelemetry.proto.collector.trace.v1.ExportTraceServiceResponse;
import io.opentelemetry.proto.collector.trace.v1.TraceServiceGrpc;
import io.opentelemetry.sdk.common.InstrumentationLibraryInfo;
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
import io.opentelemetry.sdk.metrics.data.AggregationTemporality;
import io.opentelemetry.sdk.metrics.data.LongPointData;
import io.opentelemetry.sdk.metrics.data.LongSumData;
import io.opentelemetry.sdk.metrics.data.MetricData;
import io.opentelemetry.sdk.metrics.export.IntervalMetricReader;
import io.opentelemetry.sdk.metrics.export.MetricExporter;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.testing.trace.TestSpanData;
import io.opentelemetry.sdk.trace.data.SpanData;
import io.opentelemetry.sdk.trace.data.StatusData;
import io.opentelemetry.sdk.trace.export.SpanExporter;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
class OtlpConfigTest {
private static final BlockingQueue<ExportTraceServiceRequest> otlpTraceRequests =
new LinkedBlockingDeque<>();
private static final BlockingQueue<ExportMetricsServiceRequest> otlpMetricsRequests =
new LinkedBlockingDeque<>();
private static final BlockingQueue<RequestHeaders> requestHeaders = new LinkedBlockingDeque<>();
@RegisterExtension
@Order(1)
public static final SelfSignedCertificateExtension certificate =
new SelfSignedCertificateExtension();
@RegisterExtension
@Order(2)
public static final ServerExtension server =
new ServerExtension() {
@Override
protected void configure(ServerBuilder sb) {
sb.service(
GrpcService.builder()
// OTLP spans
.addService(
new TraceServiceGrpc.TraceServiceImplBase() {
@Override
public void export(
ExportTraceServiceRequest request,
StreamObserver<ExportTraceServiceResponse> responseObserver) {
otlpTraceRequests.add(request);
responseObserver.onNext(ExportTraceServiceResponse.getDefaultInstance());
responseObserver.onCompleted();
}
})
// OTLP metrics
.addService(
new MetricsServiceGrpc.MetricsServiceImplBase() {
@Override
public void export(
ExportMetricsServiceRequest request,
StreamObserver<ExportMetricsServiceResponse> responseObserver) {
if (request.getResourceMetricsCount() > 0) {
otlpMetricsRequests.add(request);
}
responseObserver.onNext(
ExportMetricsServiceResponse.getDefaultInstance());
responseObserver.onCompleted();
}
})
.useBlockingTaskExecutor(true)
.build());
sb.decorator(
(delegate, ctx, req) -> {
requestHeaders.add(req.headers());
return delegate.serve(ctx, req);
});
sb.http(0);
sb.https(0);
sb.tls(certificate.certificateFile(), certificate.privateKeyFile());
}
};
@BeforeEach
void setUp() {
otlpTraceRequests.clear();
otlpMetricsRequests.clear();
requestHeaders.clear();
GlobalOpenTelemetry.resetForTest();
IntervalMetricReader.resetGlobalForTest();
}
@AfterEach
public void tearDown() {
GlobalOpenTelemetry.resetForTest();
IntervalMetricReader.resetGlobalForTest();
}
@Test
void configureExportersGeneral() {
Map<String, String> props = new HashMap<>();
props.put("otel.exporter.otlp.endpoint", "http://localhost:" + server.httpPort());
props.put("otel.exporter.otlp.headers", "header-key=header-value");
props.put("otel.exporter.otlp.timeout", "5s");
ConfigProperties properties = ConfigProperties.createForTest(props);
SpanExporter spanExporter = SpanExporterConfiguration.configureExporter("otlp", properties);
MetricExporter metricExporter =
MetricExporterConfiguration.configureOtlpMetrics(
properties, SdkMeterProvider.builder().build());
assertThat(spanExporter).extracting("timeoutNanos").isEqualTo(TimeUnit.SECONDS.toNanos(5));
assertThat(
spanExporter
.export(Lists.newArrayList(generateFakeSpan()))
.join(10, TimeUnit.SECONDS)
.isSuccess())
.isTrue();
assertThat(otlpTraceRequests).hasSize(1);
assertThat(requestHeaders)
.anyMatch(
headers ->
headers.contains(
":path", "/opentelemetry.proto.collector.trace.v1.TraceService/Export")
&& headers.contains("header-key", "header-value"));
assertThat(metricExporter).extracting("timeoutNanos").isEqualTo(TimeUnit.SECONDS.toNanos(5));
assertThat(
metricExporter
.export(Lists.newArrayList(generateFakeMetric()))
.join(10, TimeUnit.SECONDS)
.isSuccess())
.isTrue();
assertThat(otlpMetricsRequests).hasSize(1);
assertThat(requestHeaders)
.anyMatch(
headers ->
headers.contains(
":path", "/opentelemetry.proto.collector.metrics.v1.MetricsService/Export")
&& headers.contains("header-key", "header-value"));
}
@Test
void configureSpanExporter() {
// Set values for general and signal specific properties. Signal specific should override
// general.
Map<String, String> props = new HashMap<>();
props.put("otel.exporter.otlp.endpoint", "http://foo.bar");
props.put("otel.exporter.otlp.headers", "header-key=dummy-value");
props.put("otel.exporter.otlp.timeout", "10s");
props.put("otel.exporter.otlp.traces.endpoint", "http://localhost:" + server.httpPort());
props.put("otel.exporter.otlp.traces.headers", "header-key=header-value");
props.put("otel.exporter.otlp.traces.timeout", "5s");
SpanExporter spanExporter =
SpanExporterConfiguration.configureExporter("otlp", ConfigProperties.createForTest(props));
assertThat(spanExporter).extracting("timeoutNanos").isEqualTo(TimeUnit.SECONDS.toNanos(5));
assertThat(
spanExporter
.export(Lists.newArrayList(generateFakeSpan()))
.join(10, TimeUnit.SECONDS)
.isSuccess())
.isTrue();
assertThat(otlpTraceRequests).hasSize(1);
assertThat(requestHeaders)
.anyMatch(
headers ->
headers.contains(
":path", "/opentelemetry.proto.collector.trace.v1.TraceService/Export")
&& headers.contains("header-key", "header-value"));
}
@Test
public void configureMetricExporter() {
// Set values for general and signal specific properties. Signal specific should override
// general.
Map<String, String> props = new HashMap<>();
props.put("otel.exporter.otlp.endpoint", "http://foo.bar");
props.put("otel.exporter.otlp.headers", "header-key=dummy-value");
props.put("otel.exporter.otlp.timeout", "10s");
props.put("otel.exporter.otlp.metrics.endpoint", "http://localhost:" + server.httpPort());
props.put("otel.exporter.otlp.metrics.headers", "header-key=header-value");
props.put("otel.exporter.otlp.metrics.timeout", "5s");
MetricExporter metricExporter =
MetricExporterConfiguration.configureOtlpMetrics(
ConfigProperties.createForTest(props), SdkMeterProvider.builder().build());
assertThat(metricExporter).extracting("timeoutNanos").isEqualTo(TimeUnit.SECONDS.toNanos(5));
assertThat(
metricExporter
.export(Lists.newArrayList(generateFakeMetric()))
.join(10, TimeUnit.SECONDS)
.isSuccess())
.isTrue();
assertThat(otlpMetricsRequests).hasSize(1);
assertThat(requestHeaders)
.anyMatch(
headers ->
headers.contains(
":path", "/opentelemetry.proto.collector.metrics.v1.MetricsService/Export")
&& headers.contains("header-key", "header-value"));
}
@Test
void configureTls() {
Map<String, String> props = new HashMap<>();
props.put("otel.exporter.otlp.endpoint", "https://localhost:" + server.httpsPort());
props.put("otel.exporter.otlp.certificate", certificate.certificateFile().getAbsolutePath());
SpanExporter spanExporter =
SpanExporterConfiguration.configureExporter("otlp", ConfigProperties.createForTest(props));
assertThat(
spanExporter
.export(Lists.newArrayList(generateFakeSpan()))
.join(10, TimeUnit.SECONDS)
.isSuccess())
.isTrue();
assertThat(otlpTraceRequests).hasSize(1);
}
@Test
void configureTlsInvalidCertificatePath() {
System.setProperty("otel.exporter.otlp.certificate", Paths.get("foo", "bar", "baz").toString());
assertThatThrownBy(OpenTelemetrySdkAutoConfiguration::initialize)
.isInstanceOf(ConfigurationException.class);
}
private static SpanData generateFakeSpan() {
return TestSpanData.builder()
.setHasEnded(true)
.setName("name")
.setStartEpochNanos(MILLISECONDS.toNanos(System.currentTimeMillis()))
.setEndEpochNanos(MILLISECONDS.toNanos(System.currentTimeMillis()))
.setKind(SpanKind.SERVER)
.setStatus(StatusData.error())
.setTotalRecordedEvents(0)
.setTotalRecordedLinks(0)
.build();
}
private static MetricData generateFakeMetric() {
return MetricData.createLongSum(
Resource.empty(),
InstrumentationLibraryInfo.empty(),
"metric_name",
"metric_description",
"ms",
LongSumData.create(
false,
AggregationTemporality.CUMULATIVE,
Collections.singletonList(
LongPointData.create(
MILLISECONDS.toNanos(System.currentTimeMillis()),
MILLISECONDS.toNanos(System.currentTimeMillis()),
Attributes.of(stringKey("key"), "value"),
10))));
}
@Test
void configuresGlobal() {
System.setProperty("otel.exporter.otlp.endpoint", "http://localhost:" + server.httpPort());
System.setProperty("otel.imr.export.interval", "1s");
GlobalOpenTelemetry.get().getTracer("test").spanBuilder("test").startSpan().end();
await()
.untilAsserted(
() -> {
assertThat(otlpTraceRequests).hasSize(1);
// Not well defined how many metric exports would have happened by now, check that
// any did. Metrics are recorded by OtlpGrpcSpanExporter, BatchSpanProcessor, and
// potentially others.
assertThat(otlpMetricsRequests).isNotEmpty();
});
}
}

View File

@ -1,94 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.sdk.autoconfigure;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.awaitility.Awaitility.await;
import com.linecorp.armeria.server.ServerBuilder;
import com.linecorp.armeria.server.grpc.GrpcService;
import com.linecorp.armeria.testing.junit5.server.SelfSignedCertificateExtension;
import com.linecorp.armeria.testing.junit5.server.ServerExtension;
import io.grpc.stub.StreamObserver;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest;
import io.opentelemetry.proto.collector.trace.v1.ExportTraceServiceResponse;
import io.opentelemetry.proto.collector.trace.v1.TraceServiceGrpc;
import java.nio.file.Paths;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
class OtlpTlsTest {
private static final BlockingQueue<ExportTraceServiceRequest> otlpTraceRequests =
new LinkedBlockingDeque<>();
@RegisterExtension
@Order(1)
public static final SelfSignedCertificateExtension certificate =
new SelfSignedCertificateExtension();
@RegisterExtension
@Order(2)
public static final ServerExtension server =
new ServerExtension() {
@Override
protected void configure(ServerBuilder sb) {
sb.service(
GrpcService.builder()
// OTLP spans
.addService(
new TraceServiceGrpc.TraceServiceImplBase() {
@Override
public void export(
ExportTraceServiceRequest request,
StreamObserver<ExportTraceServiceResponse> responseObserver) {
otlpTraceRequests.add(request);
responseObserver.onNext(ExportTraceServiceResponse.getDefaultInstance());
responseObserver.onCompleted();
}
})
.useBlockingTaskExecutor(true)
.build());
sb.tls(certificate.certificateFile(), certificate.privateKeyFile());
}
};
@BeforeEach
void setUp() {
otlpTraceRequests.clear();
}
@Test
void configures() {
String endpoint = "https://localhost:" + server.httpsPort();
System.setProperty("otel.exporter.otlp.endpoint", endpoint);
System.setProperty("otel.exporter.otlp.timeout", "10000");
System.setProperty(
"otel.exporter.otlp.certificate", certificate.certificateFile().getAbsolutePath());
GlobalOpenTelemetry.get().getTracer("test").spanBuilder("test").startSpan().end();
await().untilAsserted(() -> assertThat(otlpTraceRequests).hasSize(1));
}
@Test
void invalidCertificatePath() {
String endpoint = "https://localhost:" + server.httpsPort();
System.setProperty("otel.exporter.otlp.endpoint", endpoint);
System.setProperty("otel.exporter.otlp.timeout", "10000");
System.setProperty("otel.exporter.otlp.certificate", Paths.get("foo", "bar", "baz").toString());
assertThatThrownBy(OpenTelemetrySdkAutoConfiguration::initialize)
.isInstanceOf(ConfigurationException.class);
}
}

View File

@ -52,7 +52,16 @@ public final class IntervalMetricReader {
return CompletableResultCode.ofSuccess();
}
static void resetGlobalForTest() {
/**
* Resets the globally registered {@link IntervalMetricReader} if available, or does nothing
* otherwise. This is only meant to be used from tests which need to reconfigure {@link
* IntervalMetricReader}.
*/
public static void resetGlobalForTest() {
IntervalMetricReader intervalMetricReader = globalIntervalMetricReader.get();
if (intervalMetricReader != null) {
intervalMetricReader.shutdown();
}
globalIntervalMetricReader.set(null);
}