GlobalOpenTelemetry trigger of autoconfiguration is opt-in (#5010)
* Do not initialize AutoConfiguredOpenTelemetrySdk in OpenTelemetry.get * GlobalOpenTelemetry triggers autoconfigure based on env var / system property
This commit is contained in:
parent
ea51e645dd
commit
7c6f1bd73e
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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 @@ import javax.annotation.concurrent.ThreadSafe;
|
|||
@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 @@ public final class GlobalOpenTelemetry {
|
|||
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) {
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
* <p>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}.
|
||||
*
|
||||
* <p>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> T defaultIfNull(@Nullable T value, T defaultValue) {
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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> T defaultIfNull(@Nullable T value, T defaultValue) {
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
|
||||
private ConfigUtil() {}
|
||||
}
|
|
@ -8,6 +8,7 @@ package io.opentelemetry.sdk.autoconfigure.spi.internal;
|
|||
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.HashMap;
|
|||
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}.
|
||||
*
|
||||
* <p>Environment variables take priority over {@code defaultProperties}. System properties take
|
||||
* priority over system properties.
|
||||
* priority over environment variables.
|
||||
*/
|
||||
public static DefaultConfigProperties create(Map<String, String> defaultProperties) {
|
||||
return new DefaultConfigProperties(System.getProperties(), System.getenv(), defaultProperties);
|
||||
|
@ -62,11 +62,13 @@ public final class DefaultConfigProperties implements ConfigProperties {
|
|||
Map<String, String> environmentVariables,
|
||||
Map<String, String> defaultProperties) {
|
||||
Map<String, String> 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 @@ public final class DefaultConfigProperties implements ConfigProperties {
|
|||
DefaultConfigProperties previousProperties, Map<String, String> overrides) {
|
||||
// previousProperties are already normalized, they can be copied as they are
|
||||
Map<String, String> 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 @@ public final class DefaultConfigProperties implements ConfigProperties {
|
|||
@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 final class DefaultConfigProperties implements ConfigProperties {
|
|||
@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 final class DefaultConfigProperties implements ConfigProperties {
|
|||
@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 final class DefaultConfigProperties implements ConfigProperties {
|
|||
@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 final class DefaultConfigProperties implements ConfigProperties {
|
|||
@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 final class DefaultConfigProperties implements ConfigProperties {
|
|||
|
||||
@Override
|
||||
public List<String> 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 final class DefaultConfigProperties implements ConfigProperties {
|
|||
* @throws ConfigurationException if {@code name} contains duplicate entries
|
||||
*/
|
||||
public static Set<String> getSet(ConfigProperties config, String name) {
|
||||
List<String> list = config.getList(normalize(name));
|
||||
List<String> list = config.getList(ConfigUtil.normalizePropertyKey(name));
|
||||
Set<String> set = new HashSet<>(list);
|
||||
if (set.size() != list.size()) {
|
||||
String duplicates =
|
||||
|
@ -206,7 +208,7 @@ public final class DefaultConfigProperties implements ConfigProperties {
|
|||
|
||||
@Override
|
||||
public Map<String, String> 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 @@ public final class DefaultConfigProperties implements ConfigProperties {
|
|||
// Pull everything after the last digit.
|
||||
return rawValue.substring(lastDigitIndex + 1);
|
||||
}
|
||||
|
||||
private static String normalize(String propertyName) {
|
||||
return propertyName.toLowerCase(Locale.ROOT).replace('-', '.');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,6 +66,7 @@ class ConfigErrorTest {
|
|||
@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())
|
||||
|
|
|
@ -7,14 +7,21 @@ package io.opentelemetry.sdk.autoconfigure;
|
|||
|
||||
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 @@ class AutoConfiguredOpenTelemetrySdkTest {
|
|||
// 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ import org.junit.jupiter.api.BeforeEach;
|
|||
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 @@ class OtlpGrpcConfigTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
@SetSystemProperty(key = "otel.java.global-autoconfigure.enabled", value = "true")
|
||||
void configuresGlobal() {
|
||||
System.setProperty("otel.exporter.otlp.endpoint", "https://localhost:" + server.httpsPort());
|
||||
System.setProperty(
|
||||
|
|
|
@ -36,6 +36,7 @@ import org.junit.jupiter.api.AfterEach;
|
|||
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 @@ class OtlpHttpConfigTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
@SetSystemProperty(key = "otel.java.global-autoconfigure.enabled", value = "true")
|
||||
void configuresGlobal() {
|
||||
System.setProperty("otel.exporter.otlp.protocol", "http/protobuf");
|
||||
System.setProperty(
|
||||
|
|
Loading…
Reference in New Issue