diff --git a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpDeclarativeConfigUtil.java b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpDeclarativeConfigUtil.java index ef4e2b8a099..bcb55667f50 100644 --- a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpDeclarativeConfigUtil.java +++ b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpDeclarativeConfigUtil.java @@ -10,20 +10,25 @@ import static io.opentelemetry.exporter.otlp.internal.OtlpConfigUtil.readFileBytes; import static io.opentelemetry.exporter.otlp.internal.OtlpConfigUtil.validateEndpoint; +import io.opentelemetry.api.incubator.config.ConfigProvider; +import io.opentelemetry.api.incubator.config.DeclarativeConfigException; import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; import io.opentelemetry.common.ComponentLoader; import io.opentelemetry.exporter.internal.IncubatingExporterBuilderUtil; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; +import io.opentelemetry.sdk.common.InternalTelemetryVersion; import io.opentelemetry.sdk.common.export.MemoryMode; import io.opentelemetry.sdk.common.export.RetryPolicy; import java.net.URL; import java.time.Duration; import java.util.Collections; import java.util.List; +import java.util.Locale; import java.util.function.BiConsumer; import java.util.function.Consumer; +import javax.annotation.Nullable; /** * This class is internal and is hence not for public use. Its APIs are unstable and can change at @@ -48,6 +53,7 @@ public static String getStructuredConfigOtlpProtocol(DeclarativeConfigProperties public static void configureOtlpExporterBuilder( String dataType, DeclarativeConfigProperties config, + ConfigProvider configProvider, Consumer setComponentLoader, Consumer setEndpoint, BiConsumer addHeader, @@ -57,7 +63,9 @@ public static void configureOtlpExporterBuilder( BiConsumer setClientTls, Consumer setRetryPolicy, Consumer setMemoryMode, - boolean isHttpProtobuf) { + boolean isHttpProtobuf, + Consumer internalTelemetryVersionConsumer, + Runnable setNoopMeterProvider) { setComponentLoader.accept(config.getComponentLoader()); URL endpoint = validateEndpoint(config.getString("endpoint"), isHttpProtobuf); @@ -119,6 +127,34 @@ public static void configureOtlpExporterBuilder( } IncubatingExporterBuilderUtil.configureExporterMemoryMode(config, setMemoryMode); + + // InternalTelemetryVersion defaults to disabled (i.e. null) until semantic conventions are + // stable. To disable, set a noop meter provider. + InternalTelemetryVersion telemetryVersion = getInternalTelemetryVersion(configProvider); + if (telemetryVersion == null) { + setNoopMeterProvider.run(); + } else { + internalTelemetryVersionConsumer.accept(telemetryVersion); + } + } + + @Nullable + private static InternalTelemetryVersion getInternalTelemetryVersion( + ConfigProvider configProvider) { + String internalTelemetryVersion = + configProvider.getInstrumentationConfig("otel_sdk").getString("internal_telemetry_version"); + if (internalTelemetryVersion == null) { + return null; + } + switch (internalTelemetryVersion.toLowerCase(Locale.ROOT)) { + case "legacy": + return InternalTelemetryVersion.LEGACY; + case "latest": + return InternalTelemetryVersion.LATEST; + default: + throw new DeclarativeConfigException( + "Invalid sdk telemetry version: " + internalTelemetryVersion); + } } private OtlpDeclarativeConfigUtil() {} diff --git a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpGrpcLogRecordExporterComponentProvider.java b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpGrpcLogRecordExporterComponentProvider.java index 6afb9a601cd..332787e9bf7 100644 --- a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpGrpcLogRecordExporterComponentProvider.java +++ b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpGrpcLogRecordExporterComponentProvider.java @@ -7,10 +7,12 @@ import static io.opentelemetry.exporter.otlp.internal.OtlpConfigUtil.DATA_TYPE_LOGS; +import io.opentelemetry.api.incubator.config.ConfigProvider; import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporter; import io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporterBuilder; -import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; +import io.opentelemetry.sdk.autoconfigure.spi.internal.ExtendedComponentProvider; import io.opentelemetry.sdk.logs.export.LogRecordExporter; /** @@ -19,7 +21,7 @@ *

This class is internal and is hence not for public use. Its APIs are unstable and can change * at any time. */ -public class OtlpGrpcLogRecordExporterComponentProvider implements ComponentProvider { +public class OtlpGrpcLogRecordExporterComponentProvider implements ExtendedComponentProvider { @Override public Class getType() { @@ -32,12 +34,14 @@ public String getName() { } @Override - public LogRecordExporter create(DeclarativeConfigProperties config) { + public LogRecordExporter create( + DeclarativeConfigProperties config, ConfigProvider configProvider) { OtlpGrpcLogRecordExporterBuilder builder = grpcBuilder(); OtlpDeclarativeConfigUtil.configureOtlpExporterBuilder( DATA_TYPE_LOGS, config, + configProvider, builder::setComponentLoader, builder::setEndpoint, builder::addHeader, @@ -47,7 +51,9 @@ public LogRecordExporter create(DeclarativeConfigProperties config) { builder::setClientTls, builder::setRetryPolicy, builder::setMemoryMode, - /* isHttpProtobuf= */ false); + /* isHttpProtobuf= */ false, + builder::setInternalTelemetryVersion, + () -> builder.setMeterProvider(MeterProvider::noop)); return builder.build(); } diff --git a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpGrpcMetricExporterComponentProvider.java b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpGrpcMetricExporterComponentProvider.java index cf8107f8499..6b8e2c60751 100644 --- a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpGrpcMetricExporterComponentProvider.java +++ b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpGrpcMetricExporterComponentProvider.java @@ -7,11 +7,13 @@ import static io.opentelemetry.exporter.otlp.internal.OtlpConfigUtil.DATA_TYPE_METRICS; +import io.opentelemetry.api.incubator.config.ConfigProvider; import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.exporter.internal.IncubatingExporterBuilderUtil; import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporter; import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporterBuilder; -import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; +import io.opentelemetry.sdk.autoconfigure.spi.internal.ExtendedComponentProvider; import io.opentelemetry.sdk.metrics.export.MetricExporter; /** @@ -20,7 +22,7 @@ *

This class is internal and is hence not for public use. Its APIs are unstable and can change * at any time. */ -public class OtlpGrpcMetricExporterComponentProvider implements ComponentProvider { +public class OtlpGrpcMetricExporterComponentProvider implements ExtendedComponentProvider { @Override public Class getType() { @@ -33,12 +35,13 @@ public String getName() { } @Override - public MetricExporter create(DeclarativeConfigProperties config) { + public MetricExporter create(DeclarativeConfigProperties config, ConfigProvider configProvider) { OtlpGrpcMetricExporterBuilder builder = grpcBuilder(); OtlpDeclarativeConfigUtil.configureOtlpExporterBuilder( DATA_TYPE_METRICS, config, + configProvider, builder::setComponentLoader, builder::setEndpoint, builder::addHeader, @@ -48,7 +51,9 @@ public MetricExporter create(DeclarativeConfigProperties config) { builder::setClientTls, builder::setRetryPolicy, builder::setMemoryMode, - /* isHttpProtobuf= */ false); + /* isHttpProtobuf= */ false, + builder::setInternalTelemetryVersion, + () -> builder.setMeterProvider(MeterProvider::noop)); IncubatingExporterBuilderUtil.configureOtlpAggregationTemporality( config, builder::setAggregationTemporalitySelector); IncubatingExporterBuilderUtil.configureOtlpHistogramDefaultAggregation( diff --git a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpGrpcSpanExporterComponentProvider.java b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpGrpcSpanExporterComponentProvider.java index 17995efb946..fe703101c2b 100644 --- a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpGrpcSpanExporterComponentProvider.java +++ b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpGrpcSpanExporterComponentProvider.java @@ -7,10 +7,12 @@ import static io.opentelemetry.exporter.otlp.internal.OtlpConfigUtil.DATA_TYPE_TRACES; +import io.opentelemetry.api.incubator.config.ConfigProvider; import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter; import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporterBuilder; -import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; +import io.opentelemetry.sdk.autoconfigure.spi.internal.ExtendedComponentProvider; import io.opentelemetry.sdk.trace.export.SpanExporter; /** @@ -19,7 +21,7 @@ *

This class is internal and is hence not for public use. Its APIs are unstable and can change * at any time. */ -public class OtlpGrpcSpanExporterComponentProvider implements ComponentProvider { +public class OtlpGrpcSpanExporterComponentProvider implements ExtendedComponentProvider { @Override public Class getType() { @@ -32,12 +34,13 @@ public String getName() { } @Override - public SpanExporter create(DeclarativeConfigProperties config) { + public SpanExporter create(DeclarativeConfigProperties config, ConfigProvider configProvider) { OtlpGrpcSpanExporterBuilder builder = grpcBuilder(); OtlpDeclarativeConfigUtil.configureOtlpExporterBuilder( DATA_TYPE_TRACES, config, + configProvider, builder::setComponentLoader, builder::setEndpoint, builder::addHeader, @@ -47,7 +50,9 @@ public SpanExporter create(DeclarativeConfigProperties config) { builder::setClientTls, builder::setRetryPolicy, builder::setMemoryMode, - /* isHttpProtobuf= */ false); + /* isHttpProtobuf= */ false, + builder::setInternalTelemetryVersion, + () -> builder.setMeterProvider(MeterProvider::noop)); return builder.build(); } diff --git a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpHttpLogRecordExporterComponentProvider.java b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpHttpLogRecordExporterComponentProvider.java index a2850b254ca..902583f317a 100644 --- a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpHttpLogRecordExporterComponentProvider.java +++ b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpHttpLogRecordExporterComponentProvider.java @@ -7,10 +7,12 @@ import static io.opentelemetry.exporter.otlp.internal.OtlpConfigUtil.DATA_TYPE_LOGS; +import io.opentelemetry.api.incubator.config.ConfigProvider; import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporter; import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporterBuilder; -import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; +import io.opentelemetry.sdk.autoconfigure.spi.internal.ExtendedComponentProvider; import io.opentelemetry.sdk.logs.export.LogRecordExporter; /** @@ -19,7 +21,7 @@ *

This class is internal and is hence not for public use. Its APIs are unstable and can change * at any time. */ -public class OtlpHttpLogRecordExporterComponentProvider implements ComponentProvider { +public class OtlpHttpLogRecordExporterComponentProvider implements ExtendedComponentProvider { @Override public Class getType() { @@ -32,12 +34,14 @@ public String getName() { } @Override - public LogRecordExporter create(DeclarativeConfigProperties config) { + public LogRecordExporter create( + DeclarativeConfigProperties config, ConfigProvider configProvider) { OtlpHttpLogRecordExporterBuilder builder = httpBuilder(); OtlpDeclarativeConfigUtil.configureOtlpExporterBuilder( DATA_TYPE_LOGS, config, + configProvider, builder::setComponentLoader, builder::setEndpoint, builder::addHeader, @@ -47,7 +51,9 @@ public LogRecordExporter create(DeclarativeConfigProperties config) { builder::setClientTls, builder::setRetryPolicy, builder::setMemoryMode, - /* isHttpProtobuf= */ true); + /* isHttpProtobuf= */ true, + builder::setInternalTelemetryVersion, + () -> builder.setMeterProvider(MeterProvider::noop)); return builder.build(); } diff --git a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpHttpMetricExporterComponentProvider.java b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpHttpMetricExporterComponentProvider.java index 0b59ee9380a..7c0568d57dd 100644 --- a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpHttpMetricExporterComponentProvider.java +++ b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpHttpMetricExporterComponentProvider.java @@ -7,11 +7,13 @@ import static io.opentelemetry.exporter.otlp.internal.OtlpConfigUtil.DATA_TYPE_METRICS; +import io.opentelemetry.api.incubator.config.ConfigProvider; import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.exporter.internal.IncubatingExporterBuilderUtil; import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporter; import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporterBuilder; -import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; +import io.opentelemetry.sdk.autoconfigure.spi.internal.ExtendedComponentProvider; import io.opentelemetry.sdk.metrics.export.MetricExporter; /** @@ -20,7 +22,7 @@ *

This class is internal and is hence not for public use. Its APIs are unstable and can change * at any time. */ -public class OtlpHttpMetricExporterComponentProvider implements ComponentProvider { +public class OtlpHttpMetricExporterComponentProvider implements ExtendedComponentProvider { @Override public Class getType() { @@ -33,12 +35,13 @@ public String getName() { } @Override - public MetricExporter create(DeclarativeConfigProperties config) { + public MetricExporter create(DeclarativeConfigProperties config, ConfigProvider configProvider) { OtlpHttpMetricExporterBuilder builder = httpBuilder(); OtlpDeclarativeConfigUtil.configureOtlpExporterBuilder( DATA_TYPE_METRICS, config, + configProvider, builder::setComponentLoader, builder::setEndpoint, builder::addHeader, @@ -48,7 +51,9 @@ public MetricExporter create(DeclarativeConfigProperties config) { builder::setClientTls, builder::setRetryPolicy, builder::setMemoryMode, - /* isHttpProtobuf= */ true); + /* isHttpProtobuf= */ true, + builder::setInternalTelemetryVersion, + () -> builder.setMeterProvider(MeterProvider::noop)); IncubatingExporterBuilderUtil.configureOtlpAggregationTemporality( config, builder::setAggregationTemporalitySelector); IncubatingExporterBuilderUtil.configureOtlpHistogramDefaultAggregation( diff --git a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpHttpSpanExporterComponentProvider.java b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpHttpSpanExporterComponentProvider.java index d97b2e30628..4479801d436 100644 --- a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpHttpSpanExporterComponentProvider.java +++ b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpHttpSpanExporterComponentProvider.java @@ -7,10 +7,12 @@ import static io.opentelemetry.exporter.otlp.internal.OtlpConfigUtil.DATA_TYPE_TRACES; +import io.opentelemetry.api.incubator.config.ConfigProvider; import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter; import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporterBuilder; -import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; +import io.opentelemetry.sdk.autoconfigure.spi.internal.ExtendedComponentProvider; import io.opentelemetry.sdk.trace.export.SpanExporter; /** @@ -19,7 +21,7 @@ *

This class is internal and is hence not for public use. Its APIs are unstable and can change * at any time. */ -public class OtlpHttpSpanExporterComponentProvider implements ComponentProvider { +public class OtlpHttpSpanExporterComponentProvider implements ExtendedComponentProvider { @Override public Class getType() { @@ -32,12 +34,13 @@ public String getName() { } @Override - public SpanExporter create(DeclarativeConfigProperties config) { + public SpanExporter create(DeclarativeConfigProperties config, ConfigProvider configProvider) { OtlpHttpSpanExporterBuilder builder = httpBuilder(); OtlpDeclarativeConfigUtil.configureOtlpExporterBuilder( DATA_TYPE_TRACES, config, + configProvider, builder::setComponentLoader, builder::setEndpoint, builder::addHeader, @@ -47,7 +50,9 @@ public SpanExporter create(DeclarativeConfigProperties config) { builder::setClientTls, builder::setRetryPolicy, builder::setMemoryMode, - /* isHttpProtobuf= */ true); + /* isHttpProtobuf= */ true, + builder::setInternalTelemetryVersion, + () -> builder.setMeterProvider(MeterProvider::noop)); return builder.build(); } diff --git a/sdk-extensions/autoconfigure-spi/src/main/java/io/opentelemetry/sdk/autoconfigure/spi/internal/ExtendedComponentProvider.java b/sdk-extensions/autoconfigure-spi/src/main/java/io/opentelemetry/sdk/autoconfigure/spi/internal/ExtendedComponentProvider.java new file mode 100644 index 00000000000..e2c8c194d7f --- /dev/null +++ b/sdk-extensions/autoconfigure-spi/src/main/java/io/opentelemetry/sdk/autoconfigure/spi/internal/ExtendedComponentProvider.java @@ -0,0 +1,36 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.autoconfigure.spi.internal; + +import io.opentelemetry.api.incubator.config.ConfigProvider; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; + +/** + * Extended version of {@link ComponentProvider} that allows access to the {@link ConfigProvider}. + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ +public interface ExtendedComponentProvider extends ComponentProvider { + + /** + * Configure an instance of the SDK extension component according to the {@code config}. + * + *

While this is the method called by the SDK, implementations can safely only implement {@link + * #create(DeclarativeConfigProperties)} since the default implementation delegates to it. + * + * @param config the configuration provided where the component is referenced in a configuration + * file. + * @param configProvider the configuration provider. + * @return an instance the SDK extension component + */ + Object create(DeclarativeConfigProperties config, ConfigProvider configProvider); + + @Override + default Object create(DeclarativeConfigProperties config) { + return create(config, ConfigProvider.noop()); + } +} diff --git a/sdk-extensions/autoconfigure/build.gradle.kts b/sdk-extensions/autoconfigure/build.gradle.kts index f78a0f12134..f0f89f58886 100644 --- a/sdk-extensions/autoconfigure/build.gradle.kts +++ b/sdk-extensions/autoconfigure/build.gradle.kts @@ -86,7 +86,12 @@ testing { dependencies { implementation(project(":sdk-extensions:incubator")) implementation(project(":exporters:logging")) + implementation(project(":exporters:otlp:all")) implementation(project(":sdk:testing")) + + implementation("io.opentelemetry.proto:opentelemetry-proto") + implementation("com.linecorp.armeria:armeria-junit5") + implementation("com.linecorp.armeria:armeria-grpc") } } diff --git a/sdk-extensions/autoconfigure/src/testIncubating/java/io/opentelemetry/sdk/autoconfigure/FullDeclarativeConfigTest.java b/sdk-extensions/autoconfigure/src/testIncubating/java/io/opentelemetry/sdk/autoconfigure/FullDeclarativeConfigTest.java new file mode 100644 index 00000000000..ac6b7ab4aae --- /dev/null +++ b/sdk-extensions/autoconfigure/src/testIncubating/java/io/opentelemetry/sdk/autoconfigure/FullDeclarativeConfigTest.java @@ -0,0 +1,342 @@ +/* + * 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.awaitility.Awaitility.await; + +import com.linecorp.armeria.server.ServerBuilder; +import com.linecorp.armeria.server.grpc.GrpcService; +import com.linecorp.armeria.server.logging.LoggingService; +import com.linecorp.armeria.testing.junit5.server.ServerExtension; +import io.grpc.stub.StreamObserver; +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator; +import io.opentelemetry.api.logs.Logger; +import io.opentelemetry.api.logs.Severity; +import io.opentelemetry.api.metrics.Meter; +import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator; +import io.opentelemetry.proto.collector.logs.v1.ExportLogsServiceRequest; +import io.opentelemetry.proto.collector.logs.v1.ExportLogsServiceResponse; +import io.opentelemetry.proto.collector.logs.v1.LogsServiceGrpc; +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.proto.common.v1.AnyValue; +import io.opentelemetry.proto.common.v1.KeyValue; +import io.opentelemetry.proto.metrics.v1.Metric; +import io.opentelemetry.proto.metrics.v1.ScopeMetrics; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingDeque; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +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; +import org.junit.jupiter.api.io.TempDir; + +/** Same as {@code FullConfigTest}, but using declarative configuration. */ +@SuppressWarnings("InterruptedExceptionSwallowed") +public class FullDeclarativeConfigTest { + + private static final BlockingQueue otlpTraceRequests = + new LinkedBlockingDeque<>(); + private static final BlockingQueue otlpMetricsRequests = + new LinkedBlockingDeque<>(); + private static final BlockingQueue otlpLogsRequests = + 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 responseObserver) { + otlpTraceRequests.add(request); + responseObserver.onNext(ExportTraceServiceResponse.getDefaultInstance()); + responseObserver.onCompleted(); + } + }) + // OTLP metrics + .addService( + new MetricsServiceGrpc.MetricsServiceImplBase() { + @Override + public void export( + ExportMetricsServiceRequest request, + StreamObserver responseObserver) { + if (request.getResourceMetricsCount() > 0) { + otlpMetricsRequests.add(request); + } + responseObserver.onNext( + ExportMetricsServiceResponse.getDefaultInstance()); + responseObserver.onCompleted(); + } + }) + // OTLP logs + .addService( + new LogsServiceGrpc.LogsServiceImplBase() { + @Override + public void export( + ExportLogsServiceRequest request, + StreamObserver responseObserver) { + if (request.getResourceLogsCount() > 0) { + otlpLogsRequests.add(request); + } + responseObserver.onNext(ExportLogsServiceResponse.getDefaultInstance()); + responseObserver.onCompleted(); + } + }) + .useBlockingTaskExecutor(true) + .build()); + sb.decorator(LoggingService.newDecorator()); + } + }; + + private OpenTelemetrySdk openTelemetrySdk; + + @BeforeEach + void setUp(@TempDir Path tempDir) throws IOException { + otlpTraceRequests.clear(); + otlpMetricsRequests.clear(); + otlpLogsRequests.clear(); + + String endpoint = "http://localhost:" + server.httpPort(); + String yaml = + "file_format: \"1.0-rc.1\"\n" + + "resource:\n" + + " attributes:\n" + + " - name: service.name\n" + + " value: test\n" + + "propagator:\n" + + " composite:\n" + + " - tracecontext:\n" + + " - baggage:\n" + + "tracer_provider:\n" + + " processors:\n" + + " - batch:\n" + + " schedule_delay: 60000\n" // High delay for reliable export batches after + // flushing + + " exporter:\n" + + " otlp_grpc:\n" + + " endpoint: " + + endpoint + + "\n" + + "meter_provider:\n" + + " readers:\n" + + " - periodic:\n" + + " exporter:\n" + + " otlp_grpc:\n" + + " endpoint: " + + endpoint + + "\n" + + "logger_provider:\n" + + " processors:\n" + + " - batch:\n" + + " schedule_delay: 60000\n" // High delay for reliable export batches after + // flushing + + " exporter:\n" + + " otlp_grpc:\n" + + " endpoint: " + + endpoint + + "\n" + + "instrumentation/development:\n" + + " java:\n" + + " otel_sdk:\n" + + " internal_telemetry_version: latest\n"; + + Path path = tempDir.resolve("otel-config.yaml"); + Files.write(path, yaml.getBytes(StandardCharsets.UTF_8)); + + // Initialize here so we can shutdown when done + GlobalOpenTelemetry.resetForTest(); + ConfigProperties config = + DefaultConfigProperties.createFromMap( + Collections.singletonMap("otel.experimental.config.file", path.toString())); + openTelemetrySdk = + AutoConfiguredOpenTelemetrySdk.builder() + .setConfig(config) + .setResultAsGlobal() + .build() + .getOpenTelemetrySdk(); + } + + @AfterEach + void afterEach() { + openTelemetrySdk.close(); + GlobalOpenTelemetry.resetForTest(); + } + + @Test + void configures() throws Exception { + Collection fields = + GlobalOpenTelemetry.get().getPropagators().getTextMapPropagator().fields(); + List keys = new ArrayList<>(); + keys.addAll(W3CTraceContextPropagator.getInstance().fields()); + keys.addAll(W3CBaggagePropagator.getInstance().fields()); + assertThat(fields).containsExactlyInAnyOrderElementsOf(keys); + + GlobalOpenTelemetry.get() + .getTracer("test") + .spanBuilder("test") + .startSpan() + .setAttribute("cat", "meow") + .end(); + + Meter meter = GlobalOpenTelemetry.get().getMeter("test"); + meter.counterBuilder("my-metric").build().add(1); + + Logger logger = GlobalOpenTelemetry.get().getLogsBridge().get("test"); + logger.logRecordBuilder().setBody("info log message").setSeverity(Severity.INFO).emit(); + + openTelemetrySdk.getSdkTracerProvider().forceFlush().join(10, TimeUnit.SECONDS); + openTelemetrySdk.getSdkLoggerProvider().forceFlush().join(10, TimeUnit.SECONDS); + openTelemetrySdk.getSdkMeterProvider().forceFlush().join(10, TimeUnit.SECONDS); + + await().untilAsserted(() -> assertThat(otlpTraceRequests).hasSize(1)); + + ExportTraceServiceRequest traceRequest = otlpTraceRequests.take(); + List spanResourceAttributes = + traceRequest.getResourceSpans(0).getResource().getAttributesList(); + assertHasKeyValue(spanResourceAttributes, "service.name", "test"); + io.opentelemetry.proto.trace.v1.Span span = + traceRequest.getResourceSpans(0).getScopeSpans(0).getSpans(0); + assertHasKeyValue(span.getAttributesList(), "cat", "meow"); + + // Flush again to get metric exporter metrics. + openTelemetrySdk.getSdkMeterProvider().forceFlush().join(10, TimeUnit.SECONDS); + // await on assertions since metrics may come in different order for BatchSpanProcessor, + // exporter, or the ones we created in the test. + await() + .untilAsserted( + () -> { + ExportMetricsServiceRequest metricRequest = otlpMetricsRequests.take(); + + assertThat(metricRequest.getResourceMetricsList()) + .satisfiesExactly( + resourceMetrics -> { + List metricResourceAttributes = + resourceMetrics.getResource().getAttributesList(); + assertHasKeyValue(metricResourceAttributes, "service.name", "test"); + assertThat(resourceMetrics.getScopeMetricsList()) + .anySatisfy( + scopeMetrics -> { + assertThat(scopeMetrics.getScope().getName()).isEqualTo("test"); + assertMetricNames(scopeMetrics, "my-metric"); + }) + // This verifies that MeterProvider was injected into OTLP exporters and + // the internal telemetry version was set. + .anySatisfy( + scopeMetrics -> { + assertThat(scopeMetrics.getScope().getName()) + .isEqualTo( + "io.opentelemetry.exporters.otlp_grpc_metric_exporter"); + assertMetricNames( + scopeMetrics, + "otel.sdk.exporter.metric_data_point.inflight", + "otel.sdk.exporter.operation.duration", + "otel.sdk.exporter.metric_data_point.exported"); + }) + .anySatisfy( + scopeMetrics -> { + assertThat(scopeMetrics.getScope().getName()) + .isEqualTo( + "io.opentelemetry.exporters.otlp_grpc_log_exporter"); + assertMetricNames( + scopeMetrics, + "otel.sdk.exporter.log.inflight", + "otel.sdk.exporter.operation.duration", + "otel.sdk.exporter.log.exported"); + }) + .anySatisfy( + scopeMetrics -> { + assertThat(scopeMetrics.getScope().getName()) + .isEqualTo( + "io.opentelemetry.exporters.otlp_grpc_span_exporter"); + assertMetricNames( + scopeMetrics, + "otel.sdk.exporter.span.inflight", + "otel.sdk.exporter.operation.duration", + "otel.sdk.exporter.span.exported"); + }) + .anySatisfy( + scopeMetrics -> { + assertThat(scopeMetrics.getScope().getName()) + .isEqualTo("io.opentelemetry.sdk.logs"); + assertMetricNames( + scopeMetrics, + "otel.sdk.log.created", + "otel.sdk.processor.log.processed", + "otel.sdk.processor.log.queue.capacity", + "otel.sdk.processor.log.queue.size"); + }) + .anySatisfy( + scopeMetrics -> { + assertThat(scopeMetrics.getScope().getName()) + .isEqualTo("io.opentelemetry.sdk.trace"); + assertMetricNames( + scopeMetrics, + "otel.sdk.span.live", + "otel.sdk.span.started", + "otel.sdk.processor.span.processed", + "otel.sdk.processor.span.queue.capacity", + "otel.sdk.processor.span.queue.size"); + }); + }); + }); + + await().untilAsserted(() -> assertThat(otlpLogsRequests).hasSize(1)); + ExportLogsServiceRequest logRequest = otlpLogsRequests.take(); + List logResourceAttributes = + logRequest.getResourceLogs(0).getResource().getAttributesList(); + assertHasKeyValue(logResourceAttributes, "service.name", "test"); + + assertThat(logRequest.getResourceLogs(0).getScopeLogs(0).getLogRecordsList()) + .satisfiesExactlyInAnyOrder( + logRecord -> { + assertThat(logRecord.getBody().getStringValue()).isEqualTo("info log message"); + assertThat(logRecord.getSeverityNumberValue()) + .isEqualTo(Severity.INFO.getSeverityNumber()); + }); + } + + private static void assertHasKeyValue(List keyValues, String key, String value) { + assertThat(keyValues) + .contains( + KeyValue.newBuilder() + .setKey(key) + .setValue(AnyValue.newBuilder().setStringValue(value)) + .build()); + } + + private static void assertMetricNames(ScopeMetrics scopeMetrics, String... names) { + assertThat( + scopeMetrics.getMetricsList().stream().map(Metric::getName).collect(Collectors.toSet())) + .containsExactlyInAnyOrder(names); + } +} diff --git a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/DeclarativeConfigContext.java b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/DeclarativeConfigContext.java index 404fab7bc56..beb0505f4e5 100644 --- a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/DeclarativeConfigContext.java +++ b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/DeclarativeConfigContext.java @@ -5,17 +5,24 @@ package io.opentelemetry.sdk.extension.incubator.fileconfig; +import io.opentelemetry.api.incubator.config.ConfigProvider; import io.opentelemetry.api.incubator.config.DeclarativeConfigException; import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.common.ComponentLoader; import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper; import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; +import io.opentelemetry.sdk.autoconfigure.spi.internal.ExtendedComponentProvider; +import io.opentelemetry.sdk.common.InternalTelemetryVersion; import io.opentelemetry.sdk.resources.Resource; import java.io.Closeable; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Locale; +import java.util.Objects; +import java.util.function.Consumer; +import java.util.function.Supplier; import java.util.stream.Collectors; import javax.annotation.Nullable; @@ -26,6 +33,7 @@ class DeclarativeConfigContext { private final List closeables = new ArrayList<>(); @Nullable private volatile MeterProvider meterProvider; @Nullable private Resource resource = null; + @Nullable private ConfigProvider configProvider; // Visible for testing DeclarativeConfigContext(SpiHelper spiHelper) { @@ -49,15 +57,14 @@ List getCloseables() { return Collections.unmodifiableList(closeables); } - @Nullable - public MeterProvider getMeterProvider() { - return meterProvider; - } - public void setMeterProvider(MeterProvider meterProvider) { this.meterProvider = meterProvider; } + public void setConfigProvider(ConfigProvider configProvider) { + this.configProvider = configProvider; + } + Resource getResource() { // called via reflection from io.opentelemetry.sdk.autoconfigure.IncubatingUtil if (resource == null) { @@ -70,6 +77,39 @@ void setResource(Resource resource) { this.resource = resource; } + public void setInternalTelemetry( + Consumer internalTelemetrySetter, + Consumer> meterProviderSetter) { + InternalTelemetryVersion telemetryVersion = getInternalTelemetryVersion(); + if (telemetryVersion != null) { + meterProviderSetter.accept(() -> Objects.requireNonNull(meterProvider)); + internalTelemetrySetter.accept(telemetryVersion); + } else { + meterProviderSetter.accept(MeterProvider::noop); + } + } + + @Nullable + private InternalTelemetryVersion getInternalTelemetryVersion() { + if (configProvider == null) { + return null; + } + String internalTelemetryVersion = + configProvider.getInstrumentationConfig("otel_sdk").getString("internal_telemetry_version"); + if (internalTelemetryVersion == null) { + return null; + } + switch (internalTelemetryVersion.toLowerCase(Locale.ROOT)) { + case "legacy": + return InternalTelemetryVersion.LEGACY; + case "latest": + return InternalTelemetryVersion.LATEST; + default: + throw new DeclarativeConfigException( + "Invalid sdk telemetry version: " + internalTelemetryVersion); + } + } + SpiHelper getSpiHelper() { return spiHelper; } @@ -112,9 +152,16 @@ T loadComponent(Class type, ConfigKeyValue configKeyValue) { } // Exactly one matching component provider ComponentProvider provider = matchedProviders.get(0); + ConfigProvider configProvider = this.configProvider; + if (configProvider == null) { + configProvider = ConfigProvider.noop(); + } try { - Object component = provider.create(config); + Object component = + (provider instanceof ExtendedComponentProvider) + ? ((ExtendedComponentProvider) provider).create(config, configProvider) + : provider.create(config); if (component instanceof Closeable) { closeables.add((Closeable) component); } diff --git a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/LogRecordProcessorFactory.java b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/LogRecordProcessorFactory.java index 22d53510bca..84ef72748ed 100644 --- a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/LogRecordProcessorFactory.java +++ b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/LogRecordProcessorFactory.java @@ -5,7 +5,6 @@ package io.opentelemetry.sdk.extension.incubator.fileconfig; -import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.BatchLogRecordProcessorModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.LogRecordExporterModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.LogRecordProcessorModel; @@ -15,6 +14,7 @@ import io.opentelemetry.sdk.logs.export.BatchLogRecordProcessorBuilder; import io.opentelemetry.sdk.logs.export.LogRecordExporter; import io.opentelemetry.sdk.logs.export.SimpleLogRecordProcessor; +import io.opentelemetry.sdk.logs.export.SimpleLogRecordProcessorBuilder; import java.time.Duration; final class LogRecordProcessorFactory @@ -67,10 +67,7 @@ private static LogRecordProcessor createBatchLogRecordProcessor( if (batchModel.getScheduleDelay() != null) { builder.setScheduleDelay(Duration.ofMillis(batchModel.getScheduleDelay())); } - MeterProvider meterProvider = context.getMeterProvider(); - if (meterProvider != null) { - builder.setMeterProvider(meterProvider); - } + context.setInternalTelemetry(builder::setInternalTelemetryVersion, builder::setMeterProvider); return context.addCloseable(builder.build()); } @@ -82,10 +79,8 @@ private static LogRecordProcessor createSimpleLogRecordProcessor( simpleModel.getExporter(), "simple log record processor exporter"); LogRecordExporter logRecordExporter = LogRecordExporterFactory.getInstance().create(exporterModel, context); - MeterProvider meterProvider = context.getMeterProvider(); - return context.addCloseable( - SimpleLogRecordProcessor.builder(logRecordExporter) - .setMeterProvider(() -> meterProvider) - .build()); + SimpleLogRecordProcessorBuilder builder = SimpleLogRecordProcessor.builder(logRecordExporter); + context.setInternalTelemetry(unused -> {}, builder::setMeterProvider); + return context.addCloseable(builder.build()); } } diff --git a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/LoggerProviderFactory.java b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/LoggerProviderFactory.java index 892dc65c77a..cad9f24fcc3 100644 --- a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/LoggerProviderFactory.java +++ b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/LoggerProviderFactory.java @@ -45,6 +45,8 @@ public SdkLoggerProviderBuilder create( return builder; } + context.setInternalTelemetry(unused -> {}, builder::setMeterProvider); + LogLimits logLimits = LogLimitsFactory.getInstance() .create( diff --git a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/OpenTelemetryConfigurationFactory.java b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/OpenTelemetryConfigurationFactory.java index 649df9abb98..0a3ca0ea489 100644 --- a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/OpenTelemetryConfigurationFactory.java +++ b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/OpenTelemetryConfigurationFactory.java @@ -39,6 +39,7 @@ public ExtendedOpenTelemetrySdk create( DeclarativeConfiguration.toConfigProperties( model, context.getSpiHelper().getComponentLoader()); SdkConfigProvider sdkConfigProvider = SdkConfigProvider.create(modelProperties); + context.setConfigProvider(sdkConfigProvider); OpenTelemetrySdkBuilder builder = OpenTelemetrySdkBuilderUtil.setConfigProvider( OpenTelemetrySdk.builder(), sdkConfigProvider); diff --git a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/SpanProcessorFactory.java b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/SpanProcessorFactory.java index 75b0a557cc3..1cf6a6e7021 100644 --- a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/SpanProcessorFactory.java +++ b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/SpanProcessorFactory.java @@ -5,7 +5,6 @@ package io.opentelemetry.sdk.extension.incubator.fileconfig; -import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.BatchSpanProcessorModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.SimpleSpanProcessorModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.SpanExporterModel; @@ -63,10 +62,7 @@ private static SpanProcessor createBatchLogRecordProcessor( if (batchModel.getScheduleDelay() != null) { builder.setScheduleDelay(Duration.ofMillis(batchModel.getScheduleDelay())); } - MeterProvider meterProvider = context.getMeterProvider(); - if (meterProvider != null) { - builder.setMeterProvider(() -> meterProvider); - } + context.setInternalTelemetry(builder::setInternalTelemetryVersion, builder::setMeterProvider); return context.addCloseable(builder.build()); } @@ -77,10 +73,7 @@ private static SpanProcessor createSimpleLogRecordProcessor( FileConfigUtil.requireNonNull(simpleModel.getExporter(), "simple span processor exporter"); SpanExporter spanExporter = SpanExporterFactory.getInstance().create(exporterModel, context); SimpleSpanProcessorBuilder builder = SimpleSpanProcessor.builder(spanExporter); - MeterProvider meterProvider = context.getMeterProvider(); - if (meterProvider != null) { - builder.setMeterProvider(() -> meterProvider); - } + context.setInternalTelemetry(unused -> {}, builder::setMeterProvider); return context.addCloseable(builder.build()); } } diff --git a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/TracerProviderFactory.java b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/TracerProviderFactory.java index 4e28bb25c73..4dc789bfa65 100644 --- a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/TracerProviderFactory.java +++ b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/TracerProviderFactory.java @@ -42,6 +42,8 @@ public SdkTracerProviderBuilder create( return builder; } + context.setInternalTelemetry(unused -> {}, builder::setMeterProvider); + SpanLimits spanLimits = SpanLimitsFactory.getInstance() .create(