diff --git a/api/all/build.gradle.kts b/api/all/build.gradle.kts
index 4ad64f607d3..4998fd9ddd3 100644
--- a/api/all/build.gradle.kts
+++ b/api/all/build.gradle.kts
@@ -18,3 +18,8 @@ dependencies {
testImplementation("edu.berkeley.cs.jqf:jqf-fuzz")
testImplementation("com.google.guava:guava-testlib")
}
+
+tasks.test {
+ // Configure environment variable for ConfigUtilTest
+ environment("CONFIG_KEY", "environment")
+}
diff --git a/api/all/src/main/java/io/opentelemetry/api/GlobalOpenTelemetry.java b/api/all/src/main/java/io/opentelemetry/api/GlobalOpenTelemetry.java
index 43f2c7454bd..82d2e7d42de 100644
--- a/api/all/src/main/java/io/opentelemetry/api/GlobalOpenTelemetry.java
+++ b/api/all/src/main/java/io/opentelemetry/api/GlobalOpenTelemetry.java
@@ -5,6 +5,7 @@
package io.opentelemetry.api;
+import io.opentelemetry.api.internal.ConfigUtil;
import io.opentelemetry.api.internal.GuardedBy;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.api.metrics.MeterBuilder;
@@ -44,6 +45,9 @@
@SuppressWarnings("StaticAssignmentOfThrowable")
public final class GlobalOpenTelemetry {
+ private static final String GLOBAL_AUTOCONFIGURE_ENABLED_PROPERTY =
+ "otel.java.global-autoconfigure.enabled";
+
private static final Logger logger = Logger.getLogger(GlobalOpenTelemetry.class.getName());
private static final Object mutex = new Object();
@@ -219,15 +223,29 @@ private static OpenTelemetry maybeAutoConfigureAndSetGlobal() {
return null;
}
+ // If autoconfigure module is present but global autoconfigure disabled log a warning and return
+ boolean globalAutoconfigureEnabled =
+ Boolean.parseBoolean(ConfigUtil.getString(GLOBAL_AUTOCONFIGURE_ENABLED_PROPERTY));
+ if (!globalAutoconfigureEnabled) {
+ logger.log(
+ Level.INFO,
+ "AutoConfiguredOpenTelemetrySdk found on classpath but automatic configuration is disabled."
+ + " To enable, run your JVM with -D"
+ + GLOBAL_AUTOCONFIGURE_ENABLED_PROPERTY
+ + "=true");
+ return null;
+ }
+
try {
Method initialize = openTelemetrySdkAutoConfiguration.getMethod("initialize");
Object autoConfiguredSdk = initialize.invoke(null);
Method getOpenTelemetrySdk =
openTelemetrySdkAutoConfiguration.getMethod("getOpenTelemetrySdk");
- return (OpenTelemetry) getOpenTelemetrySdk.invoke(autoConfiguredSdk);
+ return new ObfuscatedOpenTelemetry(
+ (OpenTelemetry) getOpenTelemetrySdk.invoke(autoConfiguredSdk));
} catch (NoSuchMethodException | IllegalAccessException e) {
throw new IllegalStateException(
- "OpenTelemetrySdkAutoConfiguration detected on classpath "
+ "AutoConfiguredOpenTelemetrySdk detected on classpath "
+ "but could not invoke initialize method. This is a bug in OpenTelemetry.",
e);
} catch (InvocationTargetException t) {
diff --git a/api/all/src/main/java/io/opentelemetry/api/internal/ConfigUtil.java b/api/all/src/main/java/io/opentelemetry/api/internal/ConfigUtil.java
new file mode 100644
index 00000000000..514152b2fcd
--- /dev/null
+++ b/api/all/src/main/java/io/opentelemetry/api/internal/ConfigUtil.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.api.internal;
+
+import java.util.Locale;
+import java.util.Map;
+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.
+ */
+public final class ConfigUtil {
+
+ private ConfigUtil() {}
+
+ /**
+ * 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 null
+ */
+ @Nullable
+ public static String getString(String key) {
+ String normalizedKey = normalizePropertyKey(key);
+ String systemProperty =
+ System.getProperties().entrySet().stream()
+ .filter(entry -> normalizedKey.equals(normalizePropertyKey(entry.getKey().toString())))
+ .map(entry -> entry.getValue().toString())
+ .findFirst()
+ .orElse(null);
+ if (systemProperty != null) {
+ return systemProperty;
+ }
+ return System.getenv().entrySet().stream()
+ .filter(entry -> normalizedKey.equals(normalizeEnvironmentVariableKey(entry.getKey())))
+ .map(Map.Entry::getValue)
+ .findFirst()
+ .orElse(null);
+ }
+
+ /**
+ * Normalize an environment variable key by converting to lower case and replacing "_" with ".".
+ */
+ public static String normalizeEnvironmentVariableKey(String key) {
+ return key.toLowerCase(Locale.ROOT).replace("_", ".");
+ }
+
+ /** Normalize a property key by converting to lower case and replacing "-" with ".". */
+ public static String normalizePropertyKey(String key) {
+ return key.toLowerCase(Locale.ROOT).replace("-", ".");
+ }
+
+ /** Returns defaultValue if value is null, otherwise value. This is an internal method. */
+ public static T defaultIfNull(@Nullable T value, T defaultValue) {
+ return value == null ? defaultValue : value;
+ }
+}
diff --git a/api/all/src/test/java/io/opentelemetry/api/internal/ConfigUtilTest.java b/api/all/src/test/java/io/opentelemetry/api/internal/ConfigUtilTest.java
new file mode 100644
index 00000000000..f08c0dbb78d
--- /dev/null
+++ b/api/all/src/test/java/io/opentelemetry/api/internal/ConfigUtilTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.api.internal;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.jupiter.api.Test;
+import org.junitpioneer.jupiter.SetSystemProperty;
+
+/** Relies on environment configuration in {@code ./api/all/build.gradle.kts}. */
+class ConfigUtilTest {
+
+ @Test
+ @SetSystemProperty(key = "config.key", value = "system")
+ void getString_SystemPropertyPriority() {
+ assertThat(ConfigUtil.getString("config.key")).isEqualTo("system");
+ assertThat(ConfigUtil.getString("config-key")).isEqualTo("system");
+ assertThat(ConfigUtil.getString("other.config.key")).isEqualTo(null);
+ }
+
+ @Test
+ @SetSystemProperty(key = "CONFIG-KEY", value = "system")
+ void getString_SystemPropertyNormalized() {
+ assertThat(ConfigUtil.getString("config.key")).isEqualTo("system");
+ assertThat(ConfigUtil.getString("config-key")).isEqualTo("system");
+ assertThat(ConfigUtil.getString("other.config.key")).isEqualTo(null);
+ }
+
+ @Test
+ void getString_EnvironmentVariable() {
+ assertThat(ConfigUtil.getString("config.key")).isEqualTo("environment");
+ assertThat(ConfigUtil.getString("other.config.key")).isEqualTo(null);
+ }
+
+ @Test
+ void normalizeEnvironmentVariable() {
+ assertThat(ConfigUtil.normalizeEnvironmentVariableKey("CONFIG_KEY")).isEqualTo("config.key");
+ assertThat(ConfigUtil.normalizeEnvironmentVariableKey("config_key")).isEqualTo("config.key");
+ assertThat(ConfigUtil.normalizeEnvironmentVariableKey("config-key")).isEqualTo("config-key");
+ assertThat(ConfigUtil.normalizeEnvironmentVariableKey("configkey")).isEqualTo("configkey");
+ }
+
+ @Test
+ void normalizePropertyKey() {
+ assertThat(ConfigUtil.normalizePropertyKey("CONFIG_KEY")).isEqualTo("config_key");
+ assertThat(ConfigUtil.normalizePropertyKey("CONFIG.KEY")).isEqualTo("config.key");
+ assertThat(ConfigUtil.normalizePropertyKey("config-key")).isEqualTo("config.key");
+ assertThat(ConfigUtil.normalizePropertyKey("configkey")).isEqualTo("configkey");
+ }
+
+ @Test
+ void defaultIfnull() {
+ assertThat(ConfigUtil.defaultIfNull("val1", "val2")).isEqualTo("val1");
+ assertThat(ConfigUtil.defaultIfNull(null, "val2")).isEqualTo("val2");
+ }
+}
diff --git a/sdk-extensions/autoconfigure-spi/src/main/java/io/opentelemetry/sdk/autoconfigure/spi/ConfigProperties.java b/sdk-extensions/autoconfigure-spi/src/main/java/io/opentelemetry/sdk/autoconfigure/spi/ConfigProperties.java
index 030245f2aff..4d03ca108ad 100644
--- a/sdk-extensions/autoconfigure-spi/src/main/java/io/opentelemetry/sdk/autoconfigure/spi/ConfigProperties.java
+++ b/sdk-extensions/autoconfigure-spi/src/main/java/io/opentelemetry/sdk/autoconfigure/spi/ConfigProperties.java
@@ -5,7 +5,7 @@
package io.opentelemetry.sdk.autoconfigure.spi;
-import static io.opentelemetry.sdk.autoconfigure.spi.ConfigUtil.defaultIfNull;
+import static io.opentelemetry.api.internal.ConfigUtil.defaultIfNull;
import java.time.Duration;
import java.util.List;
diff --git a/sdk-extensions/autoconfigure-spi/src/main/java/io/opentelemetry/sdk/autoconfigure/spi/ConfigUtil.java b/sdk-extensions/autoconfigure-spi/src/main/java/io/opentelemetry/sdk/autoconfigure/spi/ConfigUtil.java
deleted file mode 100644
index eb4c5cc179c..00000000000
--- a/sdk-extensions/autoconfigure-spi/src/main/java/io/opentelemetry/sdk/autoconfigure/spi/ConfigUtil.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright The OpenTelemetry Authors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package io.opentelemetry.sdk.autoconfigure.spi;
-
-import javax.annotation.Nullable;
-
-/**
- * Holder for the non-public defaultIfNull method. This serves only to mitigate the method being on
- * a public interface.
- */
-final class ConfigUtil {
-
- /** Returns defaultValue if value is null, otherwise value. This is an internal method. */
- static T defaultIfNull(@Nullable T value, T defaultValue) {
- return value == null ? defaultValue : value;
- }
-
- private ConfigUtil() {}
-}
diff --git a/sdk-extensions/autoconfigure-spi/src/main/java/io/opentelemetry/sdk/autoconfigure/spi/internal/DefaultConfigProperties.java b/sdk-extensions/autoconfigure-spi/src/main/java/io/opentelemetry/sdk/autoconfigure/spi/internal/DefaultConfigProperties.java
index a696f6f4797..c548f7586ca 100644
--- a/sdk-extensions/autoconfigure-spi/src/main/java/io/opentelemetry/sdk/autoconfigure/spi/internal/DefaultConfigProperties.java
+++ b/sdk-extensions/autoconfigure-spi/src/main/java/io/opentelemetry/sdk/autoconfigure/spi/internal/DefaultConfigProperties.java
@@ -8,6 +8,7 @@
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.joining;
+import io.opentelemetry.api.internal.ConfigUtil;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException;
import java.time.Duration;
@@ -18,7 +19,6 @@
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
-import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@@ -43,7 +43,7 @@ public final class DefaultConfigProperties implements ConfigProperties {
* and the {@code defaultProperties}.
*
* Environment variables take priority over {@code defaultProperties}. System properties take
- * priority over system properties.
+ * priority over environment variables.
*/
public static DefaultConfigProperties create(Map defaultProperties) {
return new DefaultConfigProperties(System.getProperties(), System.getenv(), defaultProperties);
@@ -62,11 +62,13 @@ private DefaultConfigProperties(
Map environmentVariables,
Map defaultProperties) {
Map config = new HashMap<>();
- defaultProperties.forEach((name, value) -> config.put(normalize(name), value));
+ defaultProperties.forEach(
+ (name, value) -> config.put(ConfigUtil.normalizePropertyKey(name), value));
environmentVariables.forEach(
- (name, value) -> config.put(name.toLowerCase(Locale.ROOT).replace('_', '.'), value));
+ (name, value) -> config.put(ConfigUtil.normalizeEnvironmentVariableKey(name), value));
systemProperties.forEach(
- (key, value) -> config.put(normalize(key.toString()), value.toString()));
+ (key, value) ->
+ config.put(ConfigUtil.normalizePropertyKey(key.toString()), value.toString()));
this.config = config;
}
@@ -75,7 +77,7 @@ private DefaultConfigProperties(
DefaultConfigProperties previousProperties, Map overrides) {
// previousProperties are already normalized, they can be copied as they are
Map config = new HashMap<>(previousProperties.config);
- overrides.forEach((name, value) -> config.put(normalize(name), value));
+ overrides.forEach((name, value) -> config.put(ConfigUtil.normalizePropertyKey(name), value));
this.config = config;
}
@@ -83,13 +85,13 @@ private DefaultConfigProperties(
@Override
@Nullable
public String getString(String name) {
- return config.get(normalize(name));
+ return config.get(ConfigUtil.normalizePropertyKey(name));
}
@Override
@Nullable
public Boolean getBoolean(String name) {
- String value = config.get(normalize(name));
+ String value = config.get(ConfigUtil.normalizePropertyKey(name));
if (value == null || value.isEmpty()) {
return null;
}
@@ -100,7 +102,7 @@ public Boolean getBoolean(String name) {
@Nullable
@SuppressWarnings("UnusedException")
public Integer getInt(String name) {
- String value = config.get(normalize(name));
+ String value = config.get(ConfigUtil.normalizePropertyKey(name));
if (value == null || value.isEmpty()) {
return null;
}
@@ -115,7 +117,7 @@ public Integer getInt(String name) {
@Nullable
@SuppressWarnings("UnusedException")
public Long getLong(String name) {
- String value = config.get(normalize(name));
+ String value = config.get(ConfigUtil.normalizePropertyKey(name));
if (value == null || value.isEmpty()) {
return null;
}
@@ -130,7 +132,7 @@ public Long getLong(String name) {
@Nullable
@SuppressWarnings("UnusedException")
public Double getDouble(String name) {
- String value = config.get(normalize(name));
+ String value = config.get(ConfigUtil.normalizePropertyKey(name));
if (value == null || value.isEmpty()) {
return null;
}
@@ -145,7 +147,7 @@ public Double getDouble(String name) {
@Nullable
@SuppressWarnings("UnusedException")
public Duration getDuration(String name) {
- String value = config.get(normalize(name));
+ String value = config.get(ConfigUtil.normalizePropertyKey(name));
if (value == null || value.isEmpty()) {
return null;
}
@@ -174,7 +176,7 @@ public Duration getDuration(String name) {
@Override
public List getList(String name) {
- String value = config.get(normalize(name));
+ String value = config.get(ConfigUtil.normalizePropertyKey(name));
if (value == null) {
return Collections.emptyList();
}
@@ -188,7 +190,7 @@ public List getList(String name) {
* @throws ConfigurationException if {@code name} contains duplicate entries
*/
public static Set getSet(ConfigProperties config, String name) {
- List list = config.getList(normalize(name));
+ List list = config.getList(ConfigUtil.normalizePropertyKey(name));
Set set = new HashSet<>(list);
if (set.size() != list.size()) {
String duplicates =
@@ -206,7 +208,7 @@ public static Set getSet(ConfigProperties config, String name) {
@Override
public Map getMap(String name) {
- return getList(normalize(name)).stream()
+ return getList(ConfigUtil.normalizePropertyKey(name)).stream()
.map(keyValuePair -> filterBlanksAndNulls(keyValuePair.split("=", 2)))
.map(
splitKeyValuePairs -> {
@@ -281,8 +283,4 @@ private static String getUnitString(String rawValue) {
// Pull everything after the last digit.
return rawValue.substring(lastDigitIndex + 1);
}
-
- private static String normalize(String propertyName) {
- return propertyName.toLowerCase(Locale.ROOT).replace('-', '.');
- }
}
diff --git a/sdk-extensions/autoconfigure/src/testConfigError/java/io/opentelemetry/sdk/autoconfigure/ConfigErrorTest.java b/sdk-extensions/autoconfigure/src/testConfigError/java/io/opentelemetry/sdk/autoconfigure/ConfigErrorTest.java
index 7e35b32a09f..66e3cc1288f 100644
--- a/sdk-extensions/autoconfigure/src/testConfigError/java/io/opentelemetry/sdk/autoconfigure/ConfigErrorTest.java
+++ b/sdk-extensions/autoconfigure/src/testConfigError/java/io/opentelemetry/sdk/autoconfigure/ConfigErrorTest.java
@@ -66,6 +66,7 @@ void invalidSampler() {
@Test
@SetSystemProperty(key = "otel.traces.sampler", value = "traceidratio")
@SetSystemProperty(key = "otel.traces.sampler.arg", value = "bar")
+ @SetSystemProperty(key = "otel.java.global-autoconfigure.enabled", value = "true")
@SuppressLogger(GlobalOpenTelemetry.class)
void globalOpenTelemetryWhenError() {
assertThat(GlobalOpenTelemetry.get())
diff --git a/sdk-extensions/autoconfigure/src/testInitializeRegistersGlobal/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkTest.java b/sdk-extensions/autoconfigure/src/testInitializeRegistersGlobal/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkTest.java
index 5b551b50ffa..02113ad5a10 100644
--- a/sdk-extensions/autoconfigure/src/testInitializeRegistersGlobal/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkTest.java
+++ b/sdk-extensions/autoconfigure/src/testInitializeRegistersGlobal/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkTest.java
@@ -7,14 +7,21 @@
import static org.assertj.core.api.Assertions.assertThat;
+import io.github.netmikey.logunit.api.LogCapturer;
import io.opentelemetry.api.GlobalOpenTelemetry;
+import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.logs.GlobalLoggerProvider;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+import org.junitpioneer.jupiter.SetSystemProperty;
class AutoConfiguredOpenTelemetrySdkTest {
+ @RegisterExtension
+ LogCapturer logs = LogCapturer.create().captureForType(GlobalOpenTelemetry.class);
+
@BeforeEach
void setUp() {
GlobalOpenTelemetry.resetForTest();
@@ -42,4 +49,24 @@ void initializeAndGet_noGlobal() {
// ObfuscatedOpenTelemetry
assertThat(GlobalOpenTelemetry.get()).isNotSameAs(sdk);
}
+
+ @Test
+ void globalOpenTelemetry_AutoConfigureDisabled() {
+ // Autoconfigure is disabled by default and enabled via otel.java.global-autoconfigure.enabled
+ assertThat(GlobalOpenTelemetry.get()).isSameAs(OpenTelemetry.noop());
+
+ logs.assertContains(
+ "AutoConfiguredOpenTelemetrySdk found on classpath but automatic configuration is disabled."
+ + " To enable, run your JVM with -Dotel.java.global-autoconfigure.enabled=true");
+ }
+
+ @Test
+ @SetSystemProperty(key = "otel.java.global-autoconfigure.enabled", value = "true")
+ void globalOpenTelemetry_AutoConfigureEnabled() {
+ assertThat(GlobalOpenTelemetry.get())
+ .extracting("delegate")
+ .isInstanceOf(OpenTelemetrySdk.class);
+
+ assertThat(logs.getEvents().size()).isEqualTo(0);
+ }
}
diff --git a/sdk-extensions/autoconfigure/src/testOtlp/java/io/opentelemetry/sdk/autoconfigure/OtlpGrpcConfigTest.java b/sdk-extensions/autoconfigure/src/testOtlp/java/io/opentelemetry/sdk/autoconfigure/OtlpGrpcConfigTest.java
index b5bbd792612..11f24a73e2f 100644
--- a/sdk-extensions/autoconfigure/src/testOtlp/java/io/opentelemetry/sdk/autoconfigure/OtlpGrpcConfigTest.java
+++ b/sdk-extensions/autoconfigure/src/testOtlp/java/io/opentelemetry/sdk/autoconfigure/OtlpGrpcConfigTest.java
@@ -41,6 +41,7 @@
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
+import org.junitpioneer.jupiter.SetSystemProperty;
class OtlpGrpcConfigTest {
@@ -318,6 +319,7 @@ void configureTlsMissingClientKeyPath() {
}
@Test
+ @SetSystemProperty(key = "otel.java.global-autoconfigure.enabled", value = "true")
void configuresGlobal() {
System.setProperty("otel.exporter.otlp.endpoint", "https://localhost:" + server.httpsPort());
System.setProperty(
diff --git a/sdk-extensions/autoconfigure/src/testOtlp/java/io/opentelemetry/sdk/autoconfigure/OtlpHttpConfigTest.java b/sdk-extensions/autoconfigure/src/testOtlp/java/io/opentelemetry/sdk/autoconfigure/OtlpHttpConfigTest.java
index f9f3bf66d4b..109e384258f 100644
--- a/sdk-extensions/autoconfigure/src/testOtlp/java/io/opentelemetry/sdk/autoconfigure/OtlpHttpConfigTest.java
+++ b/sdk-extensions/autoconfigure/src/testOtlp/java/io/opentelemetry/sdk/autoconfigure/OtlpHttpConfigTest.java
@@ -36,6 +36,7 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
+import org.junitpioneer.jupiter.SetSystemProperty;
class OtlpHttpConfigTest {
@@ -340,6 +341,7 @@ void configureTlsMissingClientKeyPath() {
}
@Test
+ @SetSystemProperty(key = "otel.java.global-autoconfigure.enabled", value = "true")
void configuresGlobal() {
System.setProperty("otel.exporter.otlp.protocol", "http/protobuf");
System.setProperty(