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()));
- }
-}