Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,16 @@ static AutoConfiguredOpenTelemetrySdk configureFromFile(
declarativeConfiguration.getMethod(
"create", openTelemetryConfiguration, ComponentLoader.class);
OpenTelemetrySdk sdk = (OpenTelemetrySdk) create.invoke(null, model, componentLoader);

Class<?> sdkConfigProvider =
Class.forName("io.opentelemetry.sdk.extension.incubator.fileconfig.SdkConfigProvider");
Method createFileConfigProvider =
sdkConfigProvider.getMethod("create", openTelemetryConfiguration, ComponentLoader.class);
ConfigProvider configProvider =
(ConfigProvider) createFileConfigProvider.invoke(null, model, componentLoader);
// Note: can't access file configuration resource without reflection so setting a dummy
// resource
return AutoConfiguredOpenTelemetrySdk.create(
sdk, Resource.getDefault(), null, configProvider);
Resource configuredResource = createResourceFromModel(model, componentLoader);
Copy link
Member

Choose a reason for hiding this comment

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

This means that we are calling ResourceFactory twice, but the alternative is for DeclarativeConfiguration#create to return a tuple of OpenTelemetrySdk and Resource such that the resource is accessible, which isn't great either.

I actually want to get rid of the incubating API to access the autoconfigured resource. Its come up in a variety of conversations (including this comment), but the TL;DR is:

The problem with accessing the autoconfigured resource is that in all the cases I've seen so far, its always been the wrong thing to do.

Copy link
Contributor Author

@robsunday robsunday Jun 13, 2025

Choose a reason for hiding this comment

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

Returning tuple from DeclarativeConfiguration#create would actually require updates of OpenTelemetryConfigurationFactory#create. This would also require some additional classes and maybe reane of OpenTelemetryConfigurationFactory.
I think instantiating resource twice is good enough solution.

Regarding the change you mentioned:

I actually want to get rid of the incubating API to access the autoconfigured resource

Do you plan to get rid of the resource property and getResource metod from AutoConfiguredOpenTelemetrySdk class?
If yes, then it may cause lots of issues in some vendor's code. We use it in our code in few places.

Copy link
Member

Choose a reason for hiding this comment

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

If yes, then it may cause lots of issues in some vendor's code. We use it in our code in few places.

I know, but why? I'm still searching for a compelling reason vendors do this. Every instance I've seen so far has been dubious.

Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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


return AutoConfiguredOpenTelemetrySdk.create(sdk, configuredResource, null, configProvider);
} catch (FileNotFoundException e) {
throw new ConfigurationException("Configuration file not found", e);
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException e) {
Expand All @@ -77,6 +77,31 @@ static AutoConfiguredOpenTelemetrySdk configureFromFile(
}
}

private static Resource createResourceFromModel(
Object openTelemetryConfigurationModel, ComponentLoader componentLoader)
throws NoSuchMethodException,
InvocationTargetException,
IllegalAccessException,
ClassNotFoundException {
Class<?> configurationModelClass =
Class.forName(
"io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel");
Method getResource = configurationModelClass.getMethod("getResource");
Object resourceModel = getResource.invoke(openTelemetryConfigurationModel);

Class<?> resourceModelClass =
Class.forName(
"io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.ResourceModel");
Class<?> declarativeConfigurationClass =
Class.forName(
"io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfiguration");
Method createResource =
declarativeConfigurationClass.getMethod(
"createResource", resourceModelClass, ComponentLoader.class);

return (Resource) createResource.invoke(null, resourceModel, componentLoader);
}

private static ConfigurationException toConfigurationException(
DeclarativeConfigException exception) {
String message = Objects.requireNonNull(exception.getMessage());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,15 +115,16 @@ void configFile_fileNotFound() {

@Test
void configFile_Valid() {
Resource expectedResource =
Resource.getDefault().toBuilder().put("service.name", "test").build();
ConfigProperties config =
DefaultConfigProperties.createFromMap(
Collections.singletonMap("otel.experimental.config.file", configFilePath.toString()));
OpenTelemetrySdk expectedSdk =
OpenTelemetrySdk.builder()
.setTracerProvider(
SdkTracerProvider.builder()
.setResource(
Resource.getDefault().toBuilder().put("service.name", "test").build())
.setResource(expectedResource)
.addSpanProcessor(SimpleSpanProcessor.create(LoggingSpanExporter.create()))
.build())
.build();
Expand All @@ -138,9 +139,7 @@ void configFile_Valid() {

assertThat(autoConfiguredOpenTelemetrySdk.getOpenTelemetrySdk().toString())
.isEqualTo(expectedSdk.toString());
// AutoConfiguredOpenTelemetrySdk#getResource() is set to a dummy value when configuring from
// file
assertThat(autoConfiguredOpenTelemetrySdk.getResource()).isEqualTo(Resource.getDefault());
assertThat(autoConfiguredOpenTelemetrySdk.getResource()).isEqualTo(expectedResource);
verify(builder, times(1)).shutdownHook(autoConfiguredOpenTelemetrySdk.getOpenTelemetrySdk());
assertThat(Runtime.getRuntime().removeShutdownHook(thread)).isTrue();
logCapturer.assertContains("Autoconfiguring from configuration file: " + configFilePath);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper;
import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.ResourceModel;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.SamplerModel;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.trace.samplers.Sampler;
import java.io.Closeable;
import java.io.IOException;
Expand Down Expand Up @@ -210,6 +212,16 @@ public static Sampler createSampler(DeclarativeConfigProperties genericSamplerMo
samplerModel);
}

/** Create a {@link Resource} from the {@code resourceModel} representing the resource config. */
public static Resource createResource(
ResourceModel resourceModel, ComponentLoader componentLoader) {
if (resourceModel == null) {
return Resource.getDefault();
}
return createAndMaybeCleanup(
ResourceFactory.getInstance(), SpiHelper.create(componentLoader), resourceModel);
}

private static YamlDeclarativeConfigProperties requireYamlDeclarativeConfigProperties(
DeclarativeConfigProperties declarativeConfigProperties) {
if (!(declarativeConfigProperties instanceof YamlDeclarativeConfigProperties)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,17 @@

import com.linecorp.armeria.testing.junit5.server.SelfSignedCertificateExtension;
import io.github.netmikey.logunit.api.LogCapturer;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.incubator.config.DeclarativeConfigException;
import io.opentelemetry.common.ComponentLoader;
import io.opentelemetry.internal.testing.CleanupExtension;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.ResourceModel;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.SpanProcessorModel;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.TracerProviderModel;
import io.opentelemetry.sdk.resources.Resource;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
Expand Down Expand Up @@ -173,4 +177,32 @@ void create_ModelCustomizer() {
+ "telemetry.sdk.name=\"opentelemetry\", "
+ "telemetry.sdk.version=\"");
}

@Test
void create_Resource() {
ResourceModel resourceModel = new ResourceModel();
resourceModel.withAttributesList("service.name=TestService");
ComponentLoader componentLoader =
SpiHelper.create(DeclarativeConfigurationCreateTest.class.getClassLoader())
.getComponentLoader();

Resource resource = DeclarativeConfiguration.createResource(resourceModel, componentLoader);

assertThat(resource).isNotNull();
assertThat(resource.getAttributes().get(AttributeKey.stringKey("service.name")))
.isEqualTo("TestService");
}

@Test
void create_defaultResource() {
ComponentLoader componentLoader =
SpiHelper.create(DeclarativeConfigurationCreateTest.class.getClassLoader())
.getComponentLoader();

Resource resource = DeclarativeConfiguration.createResource(null, componentLoader);

assertThat(resource).isNotNull();
assertThat(resource.getAttributes().get(AttributeKey.stringKey("service.name")))
.isEqualTo("unknown_service:java");
}
}
Loading