diff --git a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/EnvironmentResourceProvider.java b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/EnvironmentResourceProvider.java index 231967c833c..01d418c8cd8 100644 --- a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/EnvironmentResourceProvider.java +++ b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/EnvironmentResourceProvider.java @@ -23,8 +23,7 @@ public Resource createResource(ConfigProperties config) { @Override public int order() { - // Environment resource takes precedent over all other ResourceProviders except - // ServiceInstanceIdResourceProvider. + // Environment resource takes precedent over all other ResourceProviders. return Integer.MAX_VALUE - 1; } } diff --git a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/ResourceConfiguration.java b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/ResourceConfiguration.java index 9f1a1c4d03c..0ca26e88af2 100644 --- a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/ResourceConfiguration.java +++ b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/ResourceConfiguration.java @@ -7,6 +7,7 @@ import io.opentelemetry.common.ComponentLoader; import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper; +import io.opentelemetry.sdk.autoconfigure.resources.ServiceInstanceIdResourceProvider; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider; import io.opentelemetry.sdk.autoconfigure.spi.internal.ConditionalResourceProvider; @@ -81,6 +82,15 @@ static Resource configureResource( result = result.merge(resourceProvider.createResource(config)); } + // Apply ServiceInstanceIdResourceProvider if not disabled and service.instance.id not already + // set + if (!disabledProviders.contains(ServiceInstanceIdResourceProvider.class.getName()) + && result.getAttribute(ServiceInstanceIdResourceProvider.SERVICE_INSTANCE_ID) == null) { + ServiceInstanceIdResourceProvider serviceInstanceIdProvider = + new ServiceInstanceIdResourceProvider(); + result = result.merge(serviceInstanceIdProvider.createResource(config)); + } + result = filterAttributes(result, config); return resourceCustomizer.apply(result, config); diff --git a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/resources/ServiceInstanceIdResourceProvider.java b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/resources/ServiceInstanceIdResourceProvider.java similarity index 66% rename from sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/resources/ServiceInstanceIdResourceProvider.java rename to sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/resources/ServiceInstanceIdResourceProvider.java index d3cb764c512..fdea7351b38 100644 --- a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/resources/ServiceInstanceIdResourceProvider.java +++ b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/resources/ServiceInstanceIdResourceProvider.java @@ -3,22 +3,26 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.sdk.extension.incubator.resources; +package io.opentelemetry.sdk.autoconfigure.resources; import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider; -import io.opentelemetry.sdk.autoconfigure.spi.internal.ConditionalResourceProvider; import io.opentelemetry.sdk.resources.Resource; import java.util.UUID; /** - * A {@link ConditionalResourceProvider} for {@code service.instance.id}. It implements {@link - * ConditionalResourceProvider} rather than a plain {@link ResourceProvider} because it depends on - * the attributes discovered by the other providers. + * A {@link ResourceProvider} for {@code service.instance.id}. This provider generates a random UUID + * for {@code service.instance.id} if not already set by the user or another resource provider. The + * value is stable across calls to this provider within the same JVM instance. + * + *

This provider runs at the end of the resource provider chain (Integer.MAX_VALUE) to ensure it + * only sets service.instance.id if it hasn't been set by other providers or the user. + * + * @since 1.66.0 */ -public final class ServiceInstanceIdResourceProvider implements ConditionalResourceProvider { +public final class ServiceInstanceIdResourceProvider implements ResourceProvider { public static final AttributeKey SERVICE_INSTANCE_ID = AttributeKey.stringKey("service.instance.id"); @@ -34,11 +38,6 @@ public Resource createResource(ConfigProperties config) { return RANDOM; } - @Override - public boolean shouldApply(ConfigProperties config, Resource existing) { - return existing.getAttribute(SERVICE_INSTANCE_ID) == null; - } - @Override public int order() { // Run after environment resource provider - only set the service instance ID if it diff --git a/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/ResourceConfigurationTest.java b/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/ResourceConfigurationTest.java index d8e97e382ca..6ec42bb54d2 100644 --- a/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/ResourceConfigurationTest.java +++ b/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/ResourceConfigurationTest.java @@ -40,6 +40,9 @@ void customConfigResourceWithDisabledKeys() { props.put( "otel.resource.attributes", "food=cheesecake,drink=juice,animal= ,color=,shape=square"); props.put("otel.resource.disabled-keys", "drink"); + props.put( + "otel.java.disabled.resource.providers", + "io.opentelemetry.sdk.autoconfigure.resources.ServiceInstanceIdResourceProvider"); assertThat( ResourceConfiguration.configureResource( @@ -54,6 +57,21 @@ void customConfigResourceWithDisabledKeys() { .build()); } + @Test + void serviceInstanceIdAddedByDefault() { + Map props = new HashMap<>(); + props.put("otel.service.name", "test-service"); + + Resource result = + ResourceConfiguration.configureResource( + DefaultConfigProperties.create(props, componentLoader), + SpiHelper.create(ResourceConfigurationTest.class.getClassLoader()), + (r, c) -> r); + + // Verify service.instance.id is added by default via ServiceInstanceIdResourceProvider + assertThat(result.getAttribute(stringKey("service.instance.id"))).isNotNull(); + } + @ParameterizedTest @MethodSource("decodeResourceAttributesArgs") void decodeResourceAttributes(String input, String expectedKey, String expectedValue) { diff --git a/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/resources/ServiceInstanceIdResourceProviderTest.java b/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/resources/ServiceInstanceIdResourceProviderTest.java new file mode 100644 index 00000000000..1ebda5299db --- /dev/null +++ b/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/resources/ServiceInstanceIdResourceProviderTest.java @@ -0,0 +1,37 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.autoconfigure.resources; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; +import io.opentelemetry.sdk.resources.Resource; +import java.util.Collections; +import org.junit.jupiter.api.Test; + +class ServiceInstanceIdResourceProviderTest { + + @Test + void createResource_generatesStableValue() { + ServiceInstanceIdResourceProvider provider = new ServiceInstanceIdResourceProvider(); + DefaultConfigProperties config = DefaultConfigProperties.createFromMap(Collections.emptyMap()); + + // Multiple calls should return the same value + Resource resource1 = provider.createResource(config); + Resource resource2 = provider.createResource(config); + + assertThat(resource1.getAttribute(ServiceInstanceIdResourceProvider.SERVICE_INSTANCE_ID)) + .isNotNull(); + assertThat(resource2.getAttribute(ServiceInstanceIdResourceProvider.SERVICE_INSTANCE_ID)) + .isEqualTo(resource1.getAttribute(ServiceInstanceIdResourceProvider.SERVICE_INSTANCE_ID)); + } + + @Test + void order_returnsMaxValue() { + ServiceInstanceIdResourceProvider provider = new ServiceInstanceIdResourceProvider(); + assertThat(provider.order()).isEqualTo(Integer.MAX_VALUE); + } +} diff --git a/sdk-extensions/incubator/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider b/sdk-extensions/incubator/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider deleted file mode 100644 index 189af738dcf..00000000000 --- a/sdk-extensions/incubator/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider +++ /dev/null @@ -1 +0,0 @@ -io.opentelemetry.sdk.extension.incubator.resources.ServiceInstanceIdResourceProvider diff --git a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/resources/ServiceInstanceIdResourceProviderTest.java b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/resources/ServiceInstanceIdResourceProviderTest.java deleted file mode 100644 index b359b9fbcf0..00000000000 --- a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/resources/ServiceInstanceIdResourceProviderTest.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.sdk.extension.incubator.resources; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.google.common.collect.ImmutableMap; -import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.api.common.AttributesBuilder; -import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; -import io.opentelemetry.sdk.resources.Resource; -import java.util.Collections; -import java.util.Map; -import java.util.stream.Stream; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; - -class ServiceInstanceIdResourceProviderTest { - - @ParameterizedTest - @MethodSource("createResourceTestCases") - void createResource(String expectedValue, Map attributes) { - ServiceInstanceIdResourceProvider provider = new ServiceInstanceIdResourceProvider(); - DefaultConfigProperties config = DefaultConfigProperties.createFromMap(Collections.emptyMap()); - AttributesBuilder builder = Attributes.builder(); - attributes.forEach(builder::put); - Resource existing = Resource.create(builder.build()); - Resource resource = - provider.shouldApply(config, existing) ? provider.createResource(config) : Resource.empty(); - - String actual = - resource.getAttributes().get(ServiceInstanceIdResourceProvider.SERVICE_INSTANCE_ID); - if ("random".equals(expectedValue)) { - assertThat(actual).isNotNull(); - } else { - assertThat(actual).isEqualTo(expectedValue); - } - } - - static Stream createResourceTestCases() { - return Stream.of( - Arguments.argumentSet( - "user provided service.instance.id", - null, - ImmutableMap.of("service.instance.id", "custom")), - Arguments.argumentSet("random value", "random", Collections.emptyMap())); - } -}