diff --git a/CHANGELOG.md b/CHANGELOG.md
index ccb7a7d3abc5..87b50ea01989 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,12 @@
### ⚠️ Breaking Changes
+- Rename `otel.experimental.javascript-snippet` to
+ `otel.instrumentation.servlet.experimental.javascript-snippet` to follow naming conventions
+ ([#15339](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/15339))
+
+### ⚠️ Breaking Changes
+
- ActiveMQ Classic JMX metrics: rename attributes and metrics to align
with semantic conventions (see PR description for specifics)
([#14996](https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/14996))
diff --git a/declarative-config-bridge/build.gradle.kts b/declarative-config-bridge/build.gradle.kts
index b5ac9ec9c1c3..e02d15f1c081 100644
--- a/declarative-config-bridge/build.gradle.kts
+++ b/declarative-config-bridge/build.gradle.kts
@@ -8,6 +8,7 @@ group = "io.opentelemetry.instrumentation"
dependencies {
compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")
+ implementation(project(":instrumentation-api"))
implementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi")
implementation("io.opentelemetry:opentelemetry-api-incubator")
diff --git a/declarative-config-bridge/src/main/java/io/opentelemetry/instrumentation/config/bridge/ConfigPropertiesDeclarativeConfigProperties.java b/declarative-config-bridge/src/main/java/io/opentelemetry/instrumentation/config/bridge/ConfigPropertiesDeclarativeConfigProperties.java
new file mode 100644
index 000000000000..3626bb8de17c
--- /dev/null
+++ b/declarative-config-bridge/src/main/java/io/opentelemetry/instrumentation/config/bridge/ConfigPropertiesDeclarativeConfigProperties.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.instrumentation.config.bridge;
+
+import io.opentelemetry.api.incubator.config.ConfigProvider;
+import io.opentelemetry.instrumentation.api.internal.BridgedConfigProvider;
+import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
+
+public final class ConfigPropertiesDeclarativeConfigProperties {
+
+ private ConfigPropertiesDeclarativeConfigProperties() {}
+
+ public static ConfigProvider create(ConfigProperties configProperties) {
+ return new BridgedConfigProvider(configProperties::getString);
+ }
+}
diff --git a/declarative-config-bridge/src/test/java/io/opentelemetry/instrumentation/config/bridge/ConfigPropertiesDeclarativeConfigPropertiesTest.java b/declarative-config-bridge/src/test/java/io/opentelemetry/instrumentation/config/bridge/ConfigPropertiesDeclarativeConfigPropertiesTest.java
new file mode 100644
index 000000000000..238d89fe5aa0
--- /dev/null
+++ b/declarative-config-bridge/src/test/java/io/opentelemetry/instrumentation/config/bridge/ConfigPropertiesDeclarativeConfigPropertiesTest.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.instrumentation.config.bridge;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import io.opentelemetry.api.incubator.config.ConfigProvider;
+import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
+import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
+import org.junit.jupiter.api.Test;
+
+class ConfigPropertiesDeclarativeConfigPropertiesTest {
+
+ @Test
+ void shouldBridgeConfigProperties() {
+ ConfigProperties configProperties = mock(ConfigProperties.class);
+ when(configProperties.getString("otel.instrumentation.foo.bar")).thenReturn("baz");
+ when(configProperties.getString("otel.instrumentation.experimental.foo.bar")).thenReturn("qux");
+
+ ConfigProvider configProvider =
+ ConfigPropertiesDeclarativeConfigProperties.create(configProperties);
+ DeclarativeConfigProperties properties = configProvider.getInstrumentationConfig();
+
+ assertThat(properties.getStructured("java").getStructured("foo").getString("bar"))
+ .isEqualTo("baz");
+ assertThat(properties.getStructured("java").getStructured("foo/development").getString("bar"))
+ .isEqualTo("qux");
+ }
+}
diff --git a/docs/advanced-configuration-options.md b/docs/advanced-configuration-options.md
index 537253406497..4ae0e075c5d1 100644
--- a/docs/advanced-configuration-options.md
+++ b/docs/advanced-configuration-options.md
@@ -45,9 +45,9 @@ This feature is designed for integrating client-side monitoring.
We plan to integrate OpenTelemetry's own client-side monitoring solution by default once it's available
(see the [browser instrumentation proposal](https://github.com/open-telemetry/community/blob/main/projects/browser-phase-1.md)).
-| System property | Environment variable | Purpose |
-|--------------------------------------|--------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| otel.experimental.javascript-snippet | OTEL_EXPERIMENTAL_JAVASCRIPT_SNIPPET | Experimental setting to inject a JavaScript snippet into HTML responses after the opening `
` tag. The value should be a complete JavaScript snippet including `"` |
+| System property | Environment variable | Purpose |
+|----------------------------------------------------------------|----------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `otel.instrumentation.servlet.experimental.javascript-snippet` | `OTEL_INSTRUMENTATION_SERVLET_EXPERIMENTAL_JAVASCRIPT_SNIPPET` | Experimental setting to inject a JavaScript snippet into HTML responses after the opening `` tag. The value should be a complete JavaScript snippet including `"` |
**Important notes:**
diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-instrumentation-api.txt b/docs/apidiffs/current_vs_latest/opentelemetry-instrumentation-api.txt
index dda7a3ca5e05..787b54ffc48a 100644
--- a/docs/apidiffs/current_vs_latest/opentelemetry-instrumentation-api.txt
+++ b/docs/apidiffs/current_vs_latest/opentelemetry-instrumentation-api.txt
@@ -1,2 +1,5 @@
Comparing source compatibility of opentelemetry-instrumentation-api-2.24.0-SNAPSHOT.jar against opentelemetry-instrumentation-api-2.23.0.jar
-No changes.
\ No newline at end of file
+*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder (not serializable)
+ === CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+ GENERIC TEMPLATES: === REQUEST:java.lang.Object, === RESPONSE:java.lang.Object
+ +++ NEW METHOD: PUBLIC(+) STATIC(+) boolean isDeclarativeConfig(io.opentelemetry.api.OpenTelemetry)
diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/config/internal/CommonConfig.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/config/internal/CommonConfig.java
index abf1485fd3c4..4bbdd9335a5a 100644
--- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/config/internal/CommonConfig.java
+++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/config/internal/CommonConfig.java
@@ -175,9 +175,8 @@ private static T getFromConfigProviderOrFallback(
ValueProvider getFromConfigProvider,
T defaultValue,
Supplier fallback) {
- ConfigProvider configProvider = config.getConfigProvider();
- if (configProvider != null) {
- T value = getFromConfigProvider.get(configProvider);
+ if (config.isDeclarative()) {
+ T value = getFromConfigProvider.get(config.getConfigProvider());
return value != null ? value : defaultValue;
}
// fallback doesn't return null, so we can safely call it
diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/config/internal/InstrumentationConfig.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/config/internal/InstrumentationConfig.java
index ab1bb761aa22..3a70371cd393 100644
--- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/config/internal/InstrumentationConfig.java
+++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/config/internal/InstrumentationConfig.java
@@ -5,10 +5,13 @@
package io.opentelemetry.instrumentation.api.incubator.config.internal;
+import static io.opentelemetry.api.incubator.config.DeclarativeConfigProperties.empty;
import static java.util.Collections.emptyList;
import io.opentelemetry.api.incubator.config.ConfigProvider;
import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
+import io.opentelemetry.api.incubator.config.InstrumentationConfigUtil;
+import io.opentelemetry.instrumentation.api.internal.BridgedConfigProvider;
import java.time.Duration;
import java.util.List;
import java.util.Map;
@@ -111,13 +114,13 @@ default List getList(String name) {
Map getMap(String name, Map defaultValue);
/** Returns {@code true} if declarative configuration is used in this configuration. */
- boolean isDeclarative();
+ default boolean isDeclarative() {
+ return !(getConfigProvider() instanceof BridgedConfigProvider);
+ }
/**
* Returns a {@link DeclarativeConfigProperties} for the given node name, which is usually an
- * instrumentation name
- *
- *
Call {@link #isDeclarative()} first to check if declarative configuration is used.
+ * instrumentation name. If declarative configuration is not used, a bridge to ConfigProperties is
*
*
Declarative configuration is used to configure instrumentation properties in a declarative
* way, such as through YAML or JSON files.
@@ -125,15 +128,20 @@ default List getList(String name) {
* @param node the name of the instrumentation (e.g. "log4j"), the vendor name (e.g. "google"), or
* "common" for common Java settings that don't apply to other languages.
* @return the declarative configuration properties for the given node name
- * @throws IllegalStateException if {@link #isDeclarative()} returns {@code false}
*/
- DeclarativeConfigProperties getDeclarativeConfig(String node);
+ default DeclarativeConfigProperties getDeclarativeConfig(String node) {
+ DeclarativeConfigProperties config =
+ InstrumentationConfigUtil.javaInstrumentationConfig(getConfigProvider(), node);
+ if (config == null) {
+ // there is no declarative config for this node
+ return empty();
+ }
+ return config;
+ }
/**
- * Returns the {@link ConfigProvider} if declarative configuration is used.
- *
- * @return the {@link ConfigProvider} or {@code null} if no provider is available
+ * Returns the {@link ConfigProvider}, which is a bridge to ConfigProperties if declarative
+ * configuration is not used
*/
- @Nullable
ConfigProvider getConfigProvider();
}
diff --git a/instrumentation-api/build.gradle.kts b/instrumentation-api/build.gradle.kts
index 3b798b826de2..38dfcbf8a6a5 100644
--- a/instrumentation-api/build.gradle.kts
+++ b/instrumentation-api/build.gradle.kts
@@ -22,6 +22,7 @@ dependencies {
testImplementation("io.opentelemetry.javaagent:opentelemetry-testing-common")
testImplementation("io.opentelemetry:opentelemetry-sdk-testing")
testImplementation("io.opentelemetry:opentelemetry-exporter-common")
+ testImplementation("io.opentelemetry:opentelemetry-sdk-extension-incubator")
testImplementation("org.junit-pioneer:junit-pioneer")
jmhImplementation(project(":instrumentation-api-incubator"))
diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/InstrumenterBuilder.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/InstrumenterBuilder.java
index 422d357349a3..bd0cf9c1570e 100644
--- a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/InstrumenterBuilder.java
+++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/InstrumenterBuilder.java
@@ -9,7 +9,10 @@
import static java.util.logging.Level.WARNING;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.OpenTelemetry;
+import io.opentelemetry.api.incubator.ExtendedOpenTelemetry;
+import io.opentelemetry.api.incubator.config.InstrumentationConfigUtil;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.api.metrics.MeterBuilder;
import io.opentelemetry.api.trace.SpanKind;
@@ -20,6 +23,7 @@
import io.opentelemetry.context.propagation.TextMapGetter;
import io.opentelemetry.context.propagation.TextMapSetter;
import io.opentelemetry.instrumentation.api.internal.ConfigPropertiesUtil;
+import io.opentelemetry.instrumentation.api.internal.ConfigProviderUtil;
import io.opentelemetry.instrumentation.api.internal.EmbeddedInstrumentationProperties;
import io.opentelemetry.instrumentation.api.internal.Experimental;
import io.opentelemetry.instrumentation.api.internal.InstrumenterBuilderAccess;
@@ -48,11 +52,7 @@
public final class InstrumenterBuilder {
private static final Logger logger = Logger.getLogger(InstrumenterBuilder.class.getName());
-
- private static final SpanSuppressionStrategy spanSuppressionStrategy =
- SpanSuppressionStrategy.fromConfig(
- ConfigPropertiesUtil.getString(
- "otel.instrumentation.experimental.span-suppression-strategy"));
+ private static final boolean supportsDeclarativeConfig = supportsDeclarativeConfig();
final OpenTelemetry openTelemetry;
final String instrumentationName;
@@ -76,6 +76,20 @@ public final class InstrumenterBuilder {
boolean propagateOperationListenersToOnEnd = false;
boolean enabled = true;
+ private static boolean supportsDeclarativeConfig() {
+ try {
+ Class.forName("io.opentelemetry.api.incubator.ExtendedOpenTelemetry");
+ return true;
+ } catch (ClassNotFoundException e) {
+ // The incubator module is not available.
+ // This only happens in OpenTelemetry API instrumentation tests, where an older version of
+ // OpenTelemetry API is used that does not have ExtendedOpenTelemetry.
+ // Having the incubator module without ExtendedOpenTelemetry class should still return false
+ // for those tests to avoid a ClassNotFoundException.
+ return false;
+ }
+ }
+
static {
Experimental.internalAddOperationListenerAttributesExtractor(
(builder, operationListenerAttributesExtractor) ->
@@ -373,8 +387,19 @@ private String getSchemaUrl() {
}
SpanSuppressor buildSpanSuppressor() {
+ // otel.instrumentation.experimental.* doesn't fit the usual pattern of configuration properties
+ // for instrumentations, so we need to handle both declarative and non-declarative configs here
+ String value =
+ isDeclarativeConfig(openTelemetry)
+ ? InstrumentationConfigUtil.getOrNull(
+ ConfigProviderUtil.getConfigProvider(GlobalOpenTelemetry.get()),
+ config -> config.getString("span_suppression_strategy/development"),
+ "java",
+ "common")
+ : ConfigPropertiesUtil.getString(
+ "otel.instrumentation.experimental.span-suppression-strategy");
return new SpanSuppressors.ByContextKey(
- spanSuppressionStrategy.create(getSpanKeysFromAttributesExtractors()));
+ SpanSuppressionStrategy.fromConfig(value).create(getSpanKeysFromAttributesExtractors()));
}
private Set getSpanKeysFromAttributesExtractors() {
@@ -454,6 +479,11 @@ public void setSpanStatusExtractorCustomizer(
}
}
+ /** Returns true if the given OpenTelemetry instance supports Declarative Config. */
+ public static boolean isDeclarativeConfig(OpenTelemetry openTelemetry) {
+ return supportsDeclarativeConfig && openTelemetry instanceof ExtendedOpenTelemetry;
+ }
+
private interface InstrumenterConstructor {
Instrumenter create(InstrumenterBuilder builder);
diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/BridgedConfigProvider.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/BridgedConfigProvider.java
new file mode 100644
index 000000000000..675bc803aa09
--- /dev/null
+++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/BridgedConfigProvider.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.instrumentation.api.internal;
+
+import io.opentelemetry.api.incubator.config.ConfigProvider;
+import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
+import io.opentelemetry.common.ComponentLoader;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Function;
+import javax.annotation.Nullable;
+
+/**
+ * This class is internal and is hence not for public use. Its APIs are unstable and can change at
+ * any time.
+ */
+public final class BridgedConfigProvider implements ConfigProvider {
+
+ private final Function propertySource;
+
+ public BridgedConfigProvider(Function propertySource) {
+ this.propertySource = propertySource;
+ }
+
+ @Nullable
+ @Override
+ public DeclarativeConfigProperties getInstrumentationConfig() {
+ return createEmptyDeclarativeConfigProperties(
+ java -> {
+ if (java.equals("java")) {
+ return createEmptyDeclarativeConfigProperties(
+ name -> new BridgedDeclarativeConfigProperties(name, null, propertySource));
+ }
+ throw new UnsupportedOperationException();
+ });
+ }
+
+ private static DeclarativeConfigProperties createEmptyDeclarativeConfigProperties(
+ Function getStructuredFunc) {
+ return EmptyDeclarativeConfigPropertiesFactory.create(getStructuredFunc);
+ }
+
+ private static final class EmptyDeclarativeConfigPropertiesFactory {
+ private EmptyDeclarativeConfigPropertiesFactory() {}
+
+ static DeclarativeConfigProperties create(
+ Function getStructuredFunc) {
+ return new InnerDeclarativeConfigProperties(getStructuredFunc);
+ }
+ }
+
+ private static final class InnerDeclarativeConfigProperties
+ implements DeclarativeConfigProperties {
+ private final Function getStructuredFunc;
+
+ InnerDeclarativeConfigProperties(
+ Function getStructuredFunc) {
+ this.getStructuredFunc = getStructuredFunc;
+ }
+
+ @Nullable
+ @Override
+ public String getString(String name) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Nullable
+ @Override
+ public Boolean getBoolean(String name) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Nullable
+ @Override
+ public Integer getInt(String name) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Nullable
+ @Override
+ public Long getLong(String name) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Nullable
+ @Override
+ public Double getDouble(String name) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Nullable
+ @Override
+ public List getScalarList(String name, Class scalarType) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Nullable
+ @Override
+ public DeclarativeConfigProperties getStructured(String name) {
+ return getStructuredFunc.apply(name);
+ }
+
+ @Nullable
+ @Override
+ public List getStructuredList(String name) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Set getPropertyKeys() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ComponentLoader getComponentLoader() {
+ throw new UnsupportedOperationException();
+ }
+ }
+}
diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/BridgedDeclarativeConfigProperties.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/BridgedDeclarativeConfigProperties.java
new file mode 100644
index 000000000000..08b79e0c79eb
--- /dev/null
+++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/BridgedDeclarativeConfigProperties.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.instrumentation.api.internal;
+
+import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
+import io.opentelemetry.common.ComponentLoader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import javax.annotation.Nullable;
+
+/**
+ * This class is internal and is hence not for public use. Its APIs are unstable and can change at
+ * any time.
+ */
+public final class BridgedDeclarativeConfigProperties implements DeclarativeConfigProperties {
+ private final String node;
+ @Nullable private final BridgedDeclarativeConfigProperties parent;
+ private final Function propertySource;
+
+ public BridgedDeclarativeConfigProperties(
+ String node,
+ @Nullable BridgedDeclarativeConfigProperties parent,
+ Function propertySource) {
+ this.node = node;
+ this.parent = parent;
+ this.propertySource = propertySource;
+ }
+
+ private static List filterBlanksAndNulls(String[] values) {
+ return Arrays.stream(values)
+ .map(String::trim)
+ .filter(s -> !s.isEmpty())
+ .collect(Collectors.toList());
+ }
+
+ @Nullable
+ @Override
+ public String getString(String name) {
+ return propertySource.apply(getSystemProperty(name));
+ }
+
+ @Nullable
+ @Override
+ public Boolean getBoolean(String name) {
+ String value = getString(name);
+ return value == null ? null : Boolean.parseBoolean(value);
+ }
+
+ @Nullable
+ @Override
+ public Integer getInt(String name) {
+ String strValue = getString(name);
+ if (strValue == null) {
+ return null;
+ }
+ try {
+ return Integer.parseInt(strValue);
+ } catch (NumberFormatException ignored) {
+ return null;
+ }
+ }
+
+ @Nullable
+ @Override
+ public Long getLong(String name) {
+ String strValue = getString(name);
+ if (strValue == null) {
+ return null;
+ }
+ try {
+ return Long.getLong(strValue);
+ } catch (NumberFormatException ignored) {
+ return null;
+ }
+ }
+
+ @Nullable
+ @Override
+ public Double getDouble(String name) {
+ String strValue = getString(name);
+ if (strValue == null) {
+ return null;
+ }
+ try {
+ return Double.parseDouble(strValue);
+ } catch (NumberFormatException ignored) {
+ return null;
+ }
+ }
+
+ @SuppressWarnings("unchecked") // only String scalar type is supported
+ @Nullable
+ @Override
+ public List getScalarList(String name, Class scalarType) {
+ if (scalarType != String.class) {
+ throw new UnsupportedOperationException("Only String scalar type is supported");
+ }
+
+ String value = getString(name);
+ return value == null
+ ? null
+ : (List) BridgedDeclarativeConfigProperties.filterBlanksAndNulls(value.split(","));
+ }
+
+ @Nullable
+ @Override
+ public DeclarativeConfigProperties getStructured(String name) {
+ return new BridgedDeclarativeConfigProperties(name, this, propertySource);
+ }
+
+ @Nullable
+ @Override
+ public List getStructuredList(String name) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Set getPropertyKeys() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ComponentLoader getComponentLoader() {
+ throw new UnsupportedOperationException();
+ }
+
+ private String getSystemProperty(String name) {
+ List nodes = new ArrayList<>();
+ addSystemPropertyPathNodes(nodes);
+ nodes.add(name);
+ return toSystemProperty(nodes);
+ }
+
+ private void addSystemPropertyPathNodes(List parts) {
+ if (parent != null) {
+ parent.addSystemPropertyPathNodes(parts);
+ }
+ parts.add(node);
+ }
+
+ static String toSystemProperty(List nodes) {
+ for (int i = 0; i < nodes.size(); i++) {
+ String node = nodes.get(i);
+ if (node.endsWith("/development")) {
+ String prefix = node.contains("experimental") ? "" : "experimental.";
+ nodes.set(i, prefix + node.substring(0, node.length() - 12));
+ }
+ }
+ return "otel.instrumentation." + String.join(".", nodes).replace('_', '-');
+ }
+}
diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/ConfigPropertiesUtil.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/ConfigPropertiesUtil.java
index 862cd6b13588..1a7f3b383031 100644
--- a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/ConfigPropertiesUtil.java
+++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/ConfigPropertiesUtil.java
@@ -5,10 +5,7 @@
package io.opentelemetry.instrumentation.api.internal;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Locale;
-import java.util.stream.Collectors;
+import io.opentelemetry.api.OpenTelemetry;
import javax.annotation.Nullable;
/**
@@ -17,11 +14,25 @@
*/
public final class ConfigPropertiesUtil {
+ /**
+ * Returns the boolean value of the given property name from system properties and environment
+ * variables.
+ *
+ *
It's recommended to use {@link ConfigProviderUtil#getConfigProvider(OpenTelemetry)} instead
+ * to support Declarative Config.
+ */
public static boolean getBoolean(String propertyName, boolean defaultValue) {
String strValue = getString(propertyName);
return strValue == null ? defaultValue : Boolean.parseBoolean(strValue);
}
+ /**
+ * Returns the int value of the given property name from system properties and environment
+ * variables.
+ *
+ *
It's recommended to use {@link ConfigProviderUtil#getConfigProvider(OpenTelemetry)} instead
+ * to support Declarative Config.
+ */
public static int getInt(String propertyName, int defaultValue) {
String strValue = getString(propertyName);
if (strValue == null) {
@@ -34,37 +45,16 @@ public static int getInt(String propertyName, int defaultValue) {
}
}
+ /**
+ * Returns the string value of the given property name from system properties and environment
+ * variables.
+ *
+ *
It's recommended to use {@link ConfigProviderUtil#getConfigProvider(OpenTelemetry)} instead
+ * to support Declarative Config.
+ */
@Nullable
public static String getString(String propertyName) {
- String value = System.getProperty(propertyName);
- if (value != null) {
- return value;
- }
- return System.getenv(toEnvVarName(propertyName));
- }
-
- public static String getString(String propertyName, String defaultValue) {
- String strValue = getString(propertyName);
- return strValue == null ? defaultValue : strValue;
- }
-
- public static List getList(String propertyName, List defaultValue) {
- String value = getString(propertyName);
- if (value == null) {
- return defaultValue;
- }
- return filterBlanksAndNulls(value.split(","));
- }
-
- private static List filterBlanksAndNulls(String[] values) {
- return Arrays.stream(values)
- .map(String::trim)
- .filter(s -> !s.isEmpty())
- .collect(Collectors.toList());
- }
-
- private static String toEnvVarName(String propertyName) {
- return propertyName.toUpperCase(Locale.ROOT).replace('-', '_').replace('.', '_');
+ return ConfigUtil.getString(ConfigUtil.normalizePropertyKey(propertyName));
}
private ConfigPropertiesUtil() {}
diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/ConfigProviderUtil.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/ConfigProviderUtil.java
new file mode 100644
index 000000000000..9c2fbf2680a5
--- /dev/null
+++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/ConfigProviderUtil.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.instrumentation.api.internal;
+
+import io.opentelemetry.api.OpenTelemetry;
+import io.opentelemetry.api.incubator.ExtendedOpenTelemetry;
+import io.opentelemetry.api.incubator.config.ConfigProvider;
+
+/**
+ * This class is internal and is hence not for public use. Its APIs are unstable and can change at
+ * any time.
+ */
+public class ConfigProviderUtil {
+
+ static final BridgedConfigProvider BRIDGED_CONFIG_PROVIDER =
+ new BridgedConfigProvider(ConfigPropertiesUtil::getString);
+
+ /**
+ * Returns the ConfigProvider from declarative config if supported, otherwise returns a bridged
+ * ConfigProvider that reads from system properties or environment variables.
+ */
+ public static ConfigProvider getConfigProvider(OpenTelemetry openTelemetry) {
+ if (openTelemetry instanceof ExtendedOpenTelemetry) {
+ return ((ExtendedOpenTelemetry) openTelemetry).getConfigProvider();
+ }
+ return BRIDGED_CONFIG_PROVIDER;
+ }
+
+ private ConfigProviderUtil() {}
+}
diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/ConfigUtil.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/ConfigUtil.java
new file mode 100644
index 000000000000..701545845fba
--- /dev/null
+++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/ConfigUtil.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.instrumentation.api.internal;
+
+import java.util.ConcurrentModificationException;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Properties;
+import javax.annotation.Nullable;
+
+/**
+ * Configuration utilities.
+ *
+ *
This class is internal and is hence not for public use. Its APIs are unstable and can change
+ * at any time.
+ *
+ *
Copied from SDK
+ * because some tests target an SDK version where this class does not exist.
+ */
+final class ConfigUtil {
+
+ private ConfigUtil() {}
+
+ /**
+ * Returns a copy of system properties which is safe to iterate over.
+ *
+ *
In java 8 and android environments, iterating through system properties may trigger {@link
+ * ConcurrentModificationException}. This method ensures callers can iterate safely without risk
+ * of exception. See https://github.com/open-telemetry/opentelemetry-java/issues/6732 for details.
+ */
+ public static Properties safeSystemProperties() {
+ return (Properties) System.getProperties().clone();
+ }
+
+ /**
+ * Return the system property or environment variable for the {@code key}.
+ *
+ *
Normalize the {@code key} using {@link #normalizePropertyKey(String)}. Match to system
+ * property keys also normalized with {@link #normalizePropertyKey(String)}. Match to environment
+ * variable keys normalized with {@link #normalizeEnvironmentVariableKey(String)}. System
+ * properties take priority over environment variables.
+ *
+ * @param key the property key
+ * @return the system property if not null, or the environment variable if not null, or {@code
+ * null}
+ */
+ @Nullable
+ public static String getString(String key) {
+ String normalizedKey = normalizePropertyKey(key);
+
+ for (Map.Entry