diff --git a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkBuilder.java b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkBuilder.java index 8d7b7c46317..b13ed7a0a33 100644 --- a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkBuilder.java +++ b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkBuilder.java @@ -20,6 +20,7 @@ import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; import io.opentelemetry.sdk.autoconfigure.spi.internal.AutoConfigureListener; import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; +import io.opentelemetry.sdk.builder.internal.OpenTelemetrySdkBuilderUtil; import io.opentelemetry.sdk.logs.LogRecordProcessor; import io.opentelemetry.sdk.logs.SdkLoggerProvider; import io.opentelemetry.sdk.logs.SdkLoggerProviderBuilder; @@ -476,6 +477,10 @@ private AutoConfiguredOpenTelemetrySdk buildImpl() { try { OpenTelemetrySdkBuilder sdkBuilder = OpenTelemetrySdk.builder(); + if (INCUBATOR_AVAILABLE) { + OpenTelemetrySdkBuilderUtil.setSdkConfigProvider( + sdkBuilder, IncubatingUtil.toSdkConfigProvider(config)); + } // The propagation system is part of the API and functions in the absence of an SDK. ContextPropagators propagators = diff --git a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/IncubatingUtil.java b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/IncubatingUtil.java index 85c22c49324..9cb86ce615b 100644 --- a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/IncubatingUtil.java +++ b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/IncubatingUtil.java @@ -10,6 +10,7 @@ import io.opentelemetry.api.incubator.config.DeclarativeConfigException; import io.opentelemetry.common.ComponentLoader; import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; import io.opentelemetry.sdk.resources.Resource; import java.io.FileInputStream; @@ -139,6 +140,25 @@ static AutoConfiguredOpenTelemetrySdk createWithFactory(String name, Factory fac } } + public static Object toSdkConfigProvider(ConfigProperties configProperties) { + try { + Class sdkConfigProviderClass = + Class.forName( + "io.opentelemetry.sdk.extension.incubator.fileconfig.ConfigPropertiesBackedDeclarativeConfigProperties"); + Class configPropertiesClass = + Class.forName("io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties"); + + Method createMethod = + sdkConfigProviderClass.getMethod("createInstrumentationConfig", configPropertiesClass); + return createMethod.invoke(null, configProperties); + } catch (ClassNotFoundException + | NoSuchMethodException + | IllegalAccessException + | InvocationTargetException e) { + throw new IllegalStateException("Error creating SdkConfigProvider from ConfigProperties", e); + } + } + private static ConfigurationException toConfigurationException( DeclarativeConfigException exception) { String message = requireNonNull(exception.getMessage()); diff --git a/sdk-extensions/autoconfigure/src/testDeclarativeConfigSpi/java/io/opentelemetry/sdk/autoconfigure/DeclarativeConfigurationSpiTest.java b/sdk-extensions/autoconfigure/src/testDeclarativeConfigSpi/java/io/opentelemetry/sdk/autoconfigure/DeclarativeConfigurationSpiTest.java index 85e79634b61..f9fe92585ac 100644 --- a/sdk-extensions/autoconfigure/src/testDeclarativeConfigSpi/java/io/opentelemetry/sdk/autoconfigure/DeclarativeConfigurationSpiTest.java +++ b/sdk-extensions/autoconfigure/src/testDeclarativeConfigSpi/java/io/opentelemetry/sdk/autoconfigure/DeclarativeConfigurationSpiTest.java @@ -13,7 +13,7 @@ import io.opentelemetry.exporter.logging.LoggingSpanExporter; import io.opentelemetry.internal.testing.CleanupExtension; import io.opentelemetry.sdk.OpenTelemetrySdk; -import io.opentelemetry.sdk.extension.incubator.ExtendedOpenTelemetrySdk; +import io.opentelemetry.sdk.builder.internal.OpenTelemetrySdkBuilderUtil; import io.opentelemetry.sdk.extension.incubator.fileconfig.SdkConfigProvider; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel; import io.opentelemetry.sdk.resources.Resource; @@ -28,17 +28,20 @@ class DeclarativeConfigurationSpiTest { @Test void configFromSpi() { - ExtendedOpenTelemetrySdk expectedSdk = - ExtendedOpenTelemetrySdk.create( - OpenTelemetrySdk.builder() - .setTracerProvider( - SdkTracerProvider.builder() - .setResource( - Resource.getDefault().toBuilder().put("service.name", "test").build()) - .addSpanProcessor(SimpleSpanProcessor.create(LoggingSpanExporter.create())) - .build()) - .build(), - SdkConfigProvider.create(new OpenTelemetryConfigurationModel())); + OpenTelemetrySdk expectedSdk = + OpenTelemetrySdkBuilderUtil.setSdkConfigProvider( + OpenTelemetrySdk.builder() + .setTracerProvider( + SdkTracerProvider.builder() + .setResource( + Resource.getDefault().toBuilder() + .put("service.name", "test") + .build()) + .addSpanProcessor( + SimpleSpanProcessor.create(LoggingSpanExporter.create())) + .build()), + SdkConfigProvider.create(new OpenTelemetryConfigurationModel())) + .build(); cleanup.addCloseable(expectedSdk); AutoConfiguredOpenTelemetrySdkBuilder builder = spy(AutoConfiguredOpenTelemetrySdk.builder()); Thread thread = new Thread(); diff --git a/sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkTest.java b/sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkTest.java index 6feacb1a889..ee313c33a61 100644 --- a/sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkTest.java +++ b/sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkTest.java @@ -91,7 +91,7 @@ void globalOpenTelemetry_AutoConfigureEnabled() { private static OpenTelemetry unobfuscate(OpenTelemetry openTelemetry) { try { Field delegateField = - Class.forName("io.opentelemetry.api.GlobalOpenTelemetry$ObfuscatedOpenTelemetry") + Class.forName("io.opentelemetry.api.incubator.internal.ObfuscatedExtendedOpenTelemetry") .getDeclaredField("delegate"); delegateField.setAccessible(true); Object delegate = delegateField.get(openTelemetry); diff --git a/sdk-extensions/autoconfigure/src/testIncubating/java/io/opentelemetry/sdk/autoconfigure/DeclarativeConfigurationTest.java b/sdk-extensions/autoconfigure/src/testIncubating/java/io/opentelemetry/sdk/autoconfigure/DeclarativeConfigurationTest.java index 88bdb322b26..23dfd78cc84 100644 --- a/sdk-extensions/autoconfigure/src/testIncubating/java/io/opentelemetry/sdk/autoconfigure/DeclarativeConfigurationTest.java +++ b/sdk-extensions/autoconfigure/src/testIncubating/java/io/opentelemetry/sdk/autoconfigure/DeclarativeConfigurationTest.java @@ -29,6 +29,7 @@ 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.builder.internal.OpenTelemetrySdkBuilderUtil; import io.opentelemetry.sdk.extension.incubator.ExtendedOpenTelemetrySdk; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.trace.SdkTracerProvider; @@ -122,16 +123,18 @@ void configFile_Valid() { OpenTelemetrySdk openTelemetrySdk = autoConfiguredOpenTelemetrySdk.getOpenTelemetrySdk(); Resource resource = Resource.getDefault().toBuilder().put("service.name", "test").build(); - ExtendedOpenTelemetrySdk expectedSdk = - ExtendedOpenTelemetrySdk.create( - OpenTelemetrySdk.builder() - .setTracerProvider( - SdkTracerProvider.builder() - .setResource(resource) - .addSpanProcessor(SimpleSpanProcessor.create(LoggingSpanExporter.create())) - .build()) - .build(), - ((ExtendedOpenTelemetrySdk) openTelemetrySdk).getSdkConfigProvider()); + OpenTelemetrySdk expectedSdk = + OpenTelemetrySdkBuilderUtil.setSdkConfigProvider( + OpenTelemetrySdk.builder() + .setTracerProvider( + SdkTracerProvider.builder() + .setResource(resource) + .addSpanProcessor( + SimpleSpanProcessor.create(LoggingSpanExporter.create())) + .build()), + ((ExtendedOpenTelemetrySdk) openTelemetrySdk).getSdkConfigProvider()) + .build(); + cleanup.addCloseable(expectedSdk); assertThat(openTelemetrySdk.toString()).hasToString(expectedSdk.toString()); // AutoConfiguredOpenTelemetrySdk#getResource() is set to a dummy value when configuring from diff --git a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/ConfigPropertiesBackedDeclarativeConfigProperties.java b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/ConfigPropertiesBackedDeclarativeConfigProperties.java new file mode 100644 index 00000000000..21080778172 --- /dev/null +++ b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/ConfigPropertiesBackedDeclarativeConfigProperties.java @@ -0,0 +1,154 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.extension.incubator.fileconfig; + +import static java.util.Collections.emptySet; + +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.common.ComponentLoader; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import javax.annotation.Nullable; + +/** + * Implementation of {@link DeclarativeConfigProperties} backed by {@link ConfigProperties}. + * + *

It tracks the navigation path and only resolves to system properties at the leaf node when a + * value is actually requested. + */ +public final class ConfigPropertiesBackedDeclarativeConfigProperties + implements DeclarativeConfigProperties { + + private final ConfigProperties configProperties; + private final List path; + + public static DeclarativeConfigProperties createInstrumentationConfig( + ConfigProperties configProperties) { + return new ConfigPropertiesBackedDeclarativeConfigProperties( + configProperties, Collections.emptyList()); + } + + private ConfigPropertiesBackedDeclarativeConfigProperties( + ConfigProperties configProperties, List path) { + this.configProperties = configProperties; + this.path = path; + } + + @Nullable + @Override + public String getString(String name) { + return configProperties.getString(resolvePropertyKey(name)); + } + + @Nullable + @Override + public Boolean getBoolean(String name) { + return configProperties.getBoolean(resolvePropertyKey(name)); + } + + @Nullable + @Override + public Integer getInt(String name) { + return configProperties.getInt(resolvePropertyKey(name)); + } + + @Nullable + @Override + public Long getLong(String name) { + return configProperties.getLong(resolvePropertyKey(name)); + } + + @Nullable + @Override + public Double getDouble(String name) { + return configProperties.getDouble(resolvePropertyKey(name)); + } + + /** + * Important: this method should return null if there is no structured child with the given name, + * but unfortunately that is not implementable on top of ConfigProperties. + * + *

This will be misleading if anyone is comparing the return value to null. + */ + @Override + public DeclarativeConfigProperties getStructured(String name) { + List newPath = new ArrayList<>(path); + newPath.add(name); + return new ConfigPropertiesBackedDeclarativeConfigProperties(configProperties, newPath); + } + + @Nullable + @Override + @SuppressWarnings("unchecked") // Safe because T is known to be String via scalarType check + public List getScalarList(String name, Class scalarType) { + if (scalarType != String.class) { + return null; + } + List list = configProperties.getList(resolvePropertyKey(name)); + if (list.isEmpty()) { + return null; + } + return (List) list; + } + + @Nullable + @Override + public List getStructuredList(String name) { + return null; + } + + @Override + public Set getPropertyKeys() { + // this is not supported when using system properties based configuration + return emptySet(); + } + + @Override + public ComponentLoader getComponentLoader() { + return configProperties.getComponentLoader(); + } + + private String resolvePropertyKey(String name) { + String fullPath = pathWithName(name); + + if (!fullPath.startsWith("java.")) { + return ""; + } + + // Remove "java." prefix and translate the remaining path + String[] segments = fullPath.substring(5).split("\\."); + StringBuilder translatedPath = new StringBuilder(); + + for (int i = 0; i < segments.length; i++) { + if (i > 0) { + translatedPath.append("."); + } + translatedPath.append(translateName(segments[i])); + } + + return "otel.instrumentation." + translatedPath; + } + + private String pathWithName(String name) { + if (path.isEmpty()) { + return name; + } + return String.join(".", path) + "." + name; + } + + private static String translateName(String name) { + if (name.endsWith("/development")) { + name = name.substring(0, name.length() - "/development".length()); + if (!name.contains("experimental")) { + name = "experimental." + name; + } + } + return name.replace('_', '-'); + } +} 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 a7d0a015bfd..4b195210dc4 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 @@ -10,6 +10,7 @@ import io.opentelemetry.sdk.OpenTelemetrySdkBuilder; import io.opentelemetry.sdk.extension.incubator.ExtendedOpenTelemetrySdk; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel; +import io.opentelemetry.sdk.builder.internal.OpenTelemetrySdkBuilderUtil; import io.opentelemetry.sdk.metrics.SdkMeterProvider; import io.opentelemetry.sdk.resources.Resource; import java.util.Objects; @@ -34,7 +35,9 @@ public ExtendedOpenTelemetrySdk create( OpenTelemetryConfigurationModel model, DeclarativeConfigContext context) { SdkConfigProvider sdkConfigProvider = SdkConfigProvider.create(model, context.getSpiHelper().getComponentLoader()); - OpenTelemetrySdkBuilder builder = OpenTelemetrySdk.builder(); + OpenTelemetrySdkBuilder builder = + OpenTelemetrySdkBuilderUtil.setSdkConfigProvider( + OpenTelemetrySdk.builder(), sdkConfigProvider); String fileFormat = model.getFileFormat(); if (fileFormat == null || !SUPPORTED_FILE_FORMATS.matcher(fileFormat).matches()) { throw new DeclarativeConfigException( @@ -44,7 +47,7 @@ public ExtendedOpenTelemetrySdk create( // behavior for experimental properties. if (Objects.equals(true, model.getDisabled())) { - return ExtendedOpenTelemetrySdk.create(builder.build(), sdkConfigProvider); + return (ExtendedOpenTelemetrySdk) builder.build(); } if (model.getPropagator() != null) { @@ -92,7 +95,6 @@ public ExtendedOpenTelemetrySdk create( .build())); } - OpenTelemetrySdk openTelemetrySdk = context.addCloseable(builder.build()); - return ExtendedOpenTelemetrySdk.create(openTelemetrySdk, sdkConfigProvider); + return (ExtendedOpenTelemetrySdk) context.addCloseable(builder.build()); } } diff --git a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/SdkConfigProvider.java b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/SdkConfigProvider.java index cc1b3c2f3c1..32f4e9e98e2 100644 --- a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/SdkConfigProvider.java +++ b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/SdkConfigProvider.java @@ -19,10 +19,13 @@ public final class SdkConfigProvider implements ConfigProvider { private SdkConfigProvider( OpenTelemetryConfigurationModel model, ComponentLoader componentLoader) { - DeclarativeConfigProperties configProperties = - DeclarativeConfiguration.toConfigProperties(model, componentLoader); - this.instrumentationConfig = - configProperties.getStructured("instrumentation/development", empty()); + this( + DeclarativeConfiguration.toConfigProperties(model, componentLoader) + .getStructured("instrumentation/development", empty())); + } + + private SdkConfigProvider(DeclarativeConfigProperties instrumentationConfig) { + this.instrumentationConfig = instrumentationConfig; } /** @@ -47,6 +50,15 @@ public static SdkConfigProvider create( return new SdkConfigProvider(model, componentLoader); } + /** + * Create a no-op {@link SdkConfigProvider}. + * + * @return the no-op {@link SdkConfigProvider} + */ + public static SdkConfigProvider noop() { + return new SdkConfigProvider(DeclarativeConfigProperties.empty()); + } + @Override public DeclarativeConfigProperties getInstrumentationConfig() { return instrumentationConfig; diff --git a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/OpenTelemetryConfigurationFactoryTest.java b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/OpenTelemetryConfigurationFactoryTest.java index eefa575cc5e..def409cb452 100644 --- a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/OpenTelemetryConfigurationFactoryTest.java +++ b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/OpenTelemetryConfigurationFactoryTest.java @@ -24,6 +24,7 @@ import io.opentelemetry.internal.testing.CleanupExtension; import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper; +import io.opentelemetry.sdk.builder.internal.OpenTelemetrySdkBuilderUtil; import io.opentelemetry.sdk.extension.incubator.ExtendedOpenTelemetrySdk; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.AlwaysOnSamplerModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.AttributeNameValueModel; @@ -123,9 +124,10 @@ void create_Defaults() { List closeables = new ArrayList<>(); OpenTelemetryConfigurationModel model = new OpenTelemetryConfigurationModel().withFileFormat("1.0-rc.1"); - ExtendedOpenTelemetrySdk expectedSdk = - ExtendedOpenTelemetrySdk.create( - OpenTelemetrySdk.builder().build(), SdkConfigProvider.create(model)); + OpenTelemetrySdk expectedSdk = + OpenTelemetrySdkBuilderUtil.setSdkConfigProvider( + OpenTelemetrySdk.builder(), SdkConfigProvider.create(model)) + .build(); cleanup.addCloseable(expectedSdk); ExtendedOpenTelemetrySdk sdk = @@ -154,9 +156,10 @@ void create_Disabled() { .withExporter( new LogRecordExporterModel() .withOtlpHttp(new OtlpHttpExporterModel())))))); - ExtendedOpenTelemetrySdk expectedSdk = - ExtendedOpenTelemetrySdk.create( - OpenTelemetrySdk.builder().build(), SdkConfigProvider.create(model)); + OpenTelemetrySdk expectedSdk = + OpenTelemetrySdkBuilderUtil.setSdkConfigProvider( + OpenTelemetrySdk.builder(), SdkConfigProvider.create(model)) + .build(); cleanup.addCloseable(expectedSdk); ExtendedOpenTelemetrySdk sdk = @@ -257,63 +260,64 @@ void create_Configured() throws NoSuchFieldException, IllegalAccessException { .withName("stream-name") .withAttributeKeys(null))))); - ExtendedOpenTelemetrySdk expectedSdk = - ExtendedOpenTelemetrySdk.create( - OpenTelemetrySdk.builder() - .setPropagators( - ContextPropagators.create( - TextMapPropagator.composite( - W3CTraceContextPropagator.getInstance(), - W3CBaggagePropagator.getInstance(), - OtTracePropagator.getInstance(), - B3Propagator.injectingMultiHeaders(), - B3Propagator.injectingSingleHeader(), - JaegerPropagator.getInstance()))) - .setLoggerProvider( - SdkLoggerProvider.builder() - .setResource(expectedResource) - .setLogLimits( - () -> - LogLimits.builder() - .setMaxAttributeValueLength(1) - .setMaxNumberOfAttributes(2) + OpenTelemetrySdk expectedSdk = + OpenTelemetrySdkBuilderUtil.setSdkConfigProvider( + OpenTelemetrySdk.builder() + .setPropagators( + ContextPropagators.create( + TextMapPropagator.composite( + W3CTraceContextPropagator.getInstance(), + W3CBaggagePropagator.getInstance(), + OtTracePropagator.getInstance(), + B3Propagator.injectingMultiHeaders(), + B3Propagator.injectingSingleHeader(), + JaegerPropagator.getInstance()))) + .setLoggerProvider( + SdkLoggerProvider.builder() + .setResource(expectedResource) + .setLogLimits( + () -> + LogLimits.builder() + .setMaxAttributeValueLength(1) + .setMaxNumberOfAttributes(2) + .build()) + .addLogRecordProcessor( + io.opentelemetry.sdk.logs.export.BatchLogRecordProcessor.builder( + OtlpHttpLogRecordExporter.getDefault()) + .build()) + .build()) + .setTracerProvider( + SdkTracerProvider.builder() + .setResource(expectedResource) + .setSpanLimits( + SpanLimits.builder() + .setMaxNumberOfAttributes(1) + .setMaxAttributeValueLength(2) + .setMaxNumberOfEvents(3) + .setMaxNumberOfLinks(4) + .setMaxNumberOfAttributesPerEvent(5) + .setMaxNumberOfAttributesPerLink(6) + .build()) + .setSampler(alwaysOn()) + .addSpanProcessor( + io.opentelemetry.sdk.trace.export.BatchSpanProcessor.builder( + OtlpHttpSpanExporter.getDefault()) .build()) - .addLogRecordProcessor( - io.opentelemetry.sdk.logs.export.BatchLogRecordProcessor.builder( - OtlpHttpLogRecordExporter.getDefault()) - .build()) - .build()) - .setTracerProvider( - SdkTracerProvider.builder() - .setResource(expectedResource) - .setSpanLimits( - SpanLimits.builder() - .setMaxNumberOfAttributes(1) - .setMaxAttributeValueLength(2) - .setMaxNumberOfEvents(3) - .setMaxNumberOfLinks(4) - .setMaxNumberOfAttributesPerEvent(5) - .setMaxNumberOfAttributesPerLink(6) - .build()) - .setSampler(alwaysOn()) - .addSpanProcessor( - io.opentelemetry.sdk.trace.export.BatchSpanProcessor.builder( - OtlpHttpSpanExporter.getDefault()) - .build()) - .build()) - .setMeterProvider( - SdkMeterProvider.builder() - .setResource(expectedResource) - .registerMetricReader( - io.opentelemetry.sdk.metrics.export.PeriodicMetricReader.builder( - OtlpHttpMetricExporter.getDefault()) - .build()) - .registerView( - InstrumentSelector.builder().setName("instrument-name").build(), - View.builder().setName("stream-name").build()) - .build()) - .build(), - SdkConfigProvider.create(model)); + .build()) + .setMeterProvider( + SdkMeterProvider.builder() + .setResource(expectedResource) + .registerMetricReader( + io.opentelemetry.sdk.metrics.export.PeriodicMetricReader.builder( + OtlpHttpMetricExporter.getDefault()) + .build()) + .registerView( + InstrumentSelector.builder().setName("instrument-name").build(), + View.builder().setName("stream-name").build()) + .build()), + SdkConfigProvider.create(model)) + .build(); + cleanup.addCloseable(expectedSdk); ExtendedOpenTelemetrySdk sdk = diff --git a/sdk/all/src/main/java/io/opentelemetry/sdk/IncubatingUtil.java b/sdk/all/src/main/java/io/opentelemetry/sdk/IncubatingUtil.java new file mode 100644 index 00000000000..8694b045d3f --- /dev/null +++ b/sdk/all/src/main/java/io/opentelemetry/sdk/IncubatingUtil.java @@ -0,0 +1,57 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + * Utilities for interacting with incubating components ({@code + * io.opentelemetry:opentelemetry-api-incubator} and {@code + * io.opentelemetry:opentelemetry-sdk-extension-incubator}), which are not guaranteed to be present + * on the classpath. For all methods, callers MUST first separately reflectively confirm that the + * incubator is available on the classpath. + */ +final class IncubatingUtil { + + private IncubatingUtil() {} + + static Object noopSdkConfigProvider() { + try { + Class sdkConfigProviderClass = + Class.forName("io.opentelemetry.sdk.extension.incubator.fileconfig.SdkConfigProvider"); + Method defaultProviderMethod = sdkConfigProviderClass.getMethod("noop"); + return defaultProviderMethod.invoke(null); + } catch (ClassNotFoundException + | NoSuchMethodException + | IllegalAccessException + | InvocationTargetException e) { + throw new IllegalStateException( + "Failed to create default SdkConfigProvider from incubator", e); + } + } + + static OpenTelemetrySdk createExtendedOpenTelemetrySdk( + OpenTelemetrySdk openTelemetrySdk, Object sdkConfigProvider) { + try { + Class extendedSdkClass = + Class.forName("io.opentelemetry.sdk.extension.incubator.ExtendedOpenTelemetrySdk"); + Class sdkConfigProviderClass = + Class.forName("io.opentelemetry.sdk.extension.incubator.fileconfig.SdkConfigProvider"); + Method createMethod = + extendedSdkClass.getMethod("create", OpenTelemetrySdk.class, sdkConfigProviderClass); + return (OpenTelemetrySdk) + createMethod.invoke( + null, openTelemetrySdk, sdkConfigProviderClass.cast(sdkConfigProvider)); + } catch (ClassNotFoundException + | NoSuchMethodException + | IllegalAccessException + | InvocationTargetException e) { + throw new IllegalStateException( + "Failed to create ExtendedOpenTelemetrySdk from incubator", e); + } + } +} diff --git a/sdk/all/src/main/java/io/opentelemetry/sdk/OpenTelemetrySdkBuilder.java b/sdk/all/src/main/java/io/opentelemetry/sdk/OpenTelemetrySdkBuilder.java index f8f079acd6e..811d6097021 100644 --- a/sdk/all/src/main/java/io/opentelemetry/sdk/OpenTelemetrySdkBuilder.java +++ b/sdk/all/src/main/java/io/opentelemetry/sdk/OpenTelemetrySdkBuilder.java @@ -5,6 +5,8 @@ package io.opentelemetry.sdk; +import static java.util.Objects.requireNonNull; + import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.context.propagation.ContextPropagators; import io.opentelemetry.sdk.logs.SdkLoggerProvider; @@ -22,6 +24,26 @@ public final class OpenTelemetrySdkBuilder { @Nullable private SdkMeterProvider meterProvider; @Nullable private SdkLoggerProvider loggerProvider; + @Nullable + private Object sdkConfigProvider = + INCUBATOR_AVAILABLE ? IncubatingUtil.noopSdkConfigProvider() : null; + + private static final boolean INCUBATOR_AVAILABLE; + + static { + boolean incubatorAvailable = false; + try { + Class.forName( + "io.opentelemetry.sdk.extension.incubator.fileconfig.SdkConfigProvider", + false, + OpenTelemetrySdkBuilder.class.getClassLoader()); + incubatorAvailable = true; + } catch (ClassNotFoundException e) { + // Not available + } + INCUBATOR_AVAILABLE = incubatorAvailable; + } + /** * Package protected to disallow direct initialization. * @@ -66,7 +88,12 @@ public OpenTelemetrySdkBuilder setLoggerProvider(SdkLoggerProvider loggerProvide /** Sets the {@link ContextPropagators} to use. */ public OpenTelemetrySdkBuilder setPropagators(ContextPropagators propagators) { - this.propagators = propagators; + this.propagators = requireNonNull(propagators); + return this; + } + + OpenTelemetrySdkBuilder setSdkConfigProvider(Object sdkConfigProvider) { + this.sdkConfigProvider = requireNonNull(sdkConfigProvider); return this; } @@ -111,6 +138,11 @@ public OpenTelemetrySdk build() { loggerProvider = SdkLoggerProvider.builder().build(); } - return new OpenTelemetrySdk(tracerProvider, meterProvider, loggerProvider, propagators); + OpenTelemetrySdk openTelemetrySdk = + new OpenTelemetrySdk(tracerProvider, meterProvider, loggerProvider, propagators); + return INCUBATOR_AVAILABLE + ? IncubatingUtil.createExtendedOpenTelemetrySdk( + openTelemetrySdk, requireNonNull(sdkConfigProvider)) + : openTelemetrySdk; } } diff --git a/sdk/all/src/main/java/io/opentelemetry/sdk/builder/internal/OpenTelemetrySdkBuilderUtil.java b/sdk/all/src/main/java/io/opentelemetry/sdk/builder/internal/OpenTelemetrySdkBuilderUtil.java new file mode 100644 index 00000000000..e8b402f48b2 --- /dev/null +++ b/sdk/all/src/main/java/io/opentelemetry/sdk/builder/internal/OpenTelemetrySdkBuilderUtil.java @@ -0,0 +1,34 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.builder.internal; + +import io.opentelemetry.sdk.OpenTelemetrySdkBuilder; +import java.lang.reflect.Method; + +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ +public final class OpenTelemetrySdkBuilderUtil { + + private OpenTelemetrySdkBuilderUtil() {} + + public static OpenTelemetrySdkBuilder setSdkConfigProvider( + OpenTelemetrySdkBuilder builder, Object sdkConfigProvider) { + try { + Method method = + OpenTelemetrySdkBuilder.class.getDeclaredMethod("setSdkConfigProvider", Object.class); + method.setAccessible(true); + method.invoke(builder, sdkConfigProvider); + return builder; + } catch (NoSuchMethodException + | IllegalAccessException + | java.lang.reflect.InvocationTargetException e) { + throw new IllegalStateException( + "Error calling setSdkConfigProvider on OpenTelemetrySdkBuilder", e); + } + } +}