Răsfoiți Sursa

Cleanup Config & ConfigBuilder API (#5733)

* Cleanup Config & ConfigBuilder API

* errorprone

* errorprone & spotless

* spotless
Mateusz Rzeszutek 2 ani în urmă
părinte
comite
65717dae3e
23 a modificat fișierele cu 278 adăugiri și 199 ștergeri
  1. 25 1
      instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/config/Config.java
  2. 64 4
      instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/config/ConfigBuilder.java
  3. 12 8
      instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/ContextPropagationDebug.java
  4. 1 1
      instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/SupportabilityMetrics.java
  5. 0 63
      instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/config/ConfigTest.java
  6. 1 4
      instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/internal/SupportabilityMetricsTest.java
  7. 2 3
      instrumentation/external-annotations/javaagent-unit-tests/src/test/groovy/IncludeTest.groovy
  8. 1 1
      instrumentation/grizzly-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grizzly/GrizzlyInstrumentationModule.java
  9. 1 0
      instrumentation/oshi/javaagent/build.gradle.kts
  10. 3 2
      instrumentation/oshi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/oshi/OshiMetricsInstaller.java
  11. 1 0
      instrumentation/runtime-metrics/javaagent/build.gradle.kts
  12. 4 2
      instrumentation/runtime-metrics/javaagent/src/main/java/io/opentelemetry/instrumentation/javaagent/runtimemetrics/RuntimeMetricsInstaller.java
  13. 2 16
      instrumentation/spring/spring-batch-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/batch/SpringBatchInstrumentationConfig.java
  14. 2 3
      instrumentation/spring/spring-batch-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/batch/SpringBatchInstrumentationModule.java
  15. 20 9
      javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/instrumentation/InstrumentationModule.java
  16. 0 74
      javaagent-extension-api/src/test/groovy/io/opentelemetry/javaagent/extension/instrumentation/InstrumentationModuleTest.groovy
  17. 2 1
      javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentInstaller.java
  18. 2 1
      javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentTracerProviderConfigurer.java
  19. 45 0
      javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/config/AgentConfig.java
  20. 4 4
      javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/config/ConfigInitializer.java
  21. 4 1
      javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/instrumentation/InstrumentationModuleInstaller.java
  22. 81 0
      javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/config/AgentConfigTest.java
  23. 1 1
      javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/ignore/UserExcludedClassesConfigurerTest.java

+ 25 - 1
instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/config/Config.java

@@ -9,6 +9,7 @@ import static java.util.Objects.requireNonNull;
 import static java.util.logging.Level.FINE;
 
 import com.google.auto.value.AutoValue;
+import io.opentelemetry.instrumentation.api.annotations.UnstableApi;
 import java.time.Duration;
 import java.util.List;
 import java.util.Map;
@@ -35,6 +36,7 @@ public abstract class Config {
   @Nullable private static volatile Config instance = null;
 
   /** Start building a new {@link Config} instance. */
+  @SuppressWarnings("deprecation")
   public static ConfigBuilder builder() {
     return new ConfigBuilder();
   }
@@ -50,7 +52,11 @@ public abstract class Config {
    * Sets the agent configuration singleton. This method is only supposed to be called once, from
    * the agent classloader just before the first instrumentation is loaded (and before {@link
    * Config#get()} is used for the first time).
+   *
+   * <p>This method is internal and is hence not for public use. Its API is unstable and can change
+   * at any time.
    */
+  @UnstableApi
   public static void internalInitializeConfig(Config config) {
     if (instance != null) {
       logger.warning("Config#INSTANCE was already set earlier");
@@ -65,7 +71,7 @@ public abstract class Config {
       // this should only happen in library instrumentation
       //
       // no need to synchronize because worst case is creating instance more than once
-      instance = builder().readEnvironmentVariables().readSystemProperties().build();
+      instance = builder().addEnvironmentVariables().addSystemProperties().build();
     }
     return instance;
   }
@@ -186,11 +192,23 @@ public abstract class Config {
     return getAllProperties().getOrDefault(NamingConvention.DOT.normalize(name), defaultValue);
   }
 
+  /**
+   * Returns {@code true} when instrumentation is enabled.
+   *
+   * @deprecated This method will be removed.
+   */
+  @Deprecated
   public boolean isInstrumentationEnabled(
       Iterable<String> instrumentationNames, boolean defaultEnabled) {
     return isInstrumentationPropertyEnabled(instrumentationNames, "enabled", defaultEnabled);
   }
 
+  /**
+   * Returns {@code true} when instrumentation is enabled.
+   *
+   * @deprecated This method will be removed.
+   */
+  @Deprecated
   public boolean isInstrumentationPropertyEnabled(
       Iterable<String> instrumentationNames, String suffix, boolean defaultEnabled) {
     // If default is enabled, we want to enable individually,
@@ -209,6 +227,12 @@ public abstract class Config {
     return anyEnabled;
   }
 
+  /**
+   * Returns {@code true} when agent runs in debug mode.
+   *
+   * @deprecated This method will be removed.
+   */
+  @Deprecated
   public boolean isAgentDebugEnabled() {
     return getBoolean("otel.javaagent.debug", false);
   }

+ 64 - 4
instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/config/ConfigBuilder.java

@@ -10,34 +10,93 @@ import java.util.HashMap;
 import java.util.Map;
 import java.util.Properties;
 
+/** A builder of a {@link Config}. */
 public final class ConfigBuilder {
 
   private final Map<String, String> allProperties = new HashMap<>();
 
+  /**
+   * Constructs a new {@link ConfigBuilder}.
+   *
+   * @deprecated This method will be made package-private in the next release. Use {@link
+   *     Config#builder()} instead.
+   */
+  @Deprecated
+  public ConfigBuilder() {}
+
+  /** Adds a single property named to the config. */
   public ConfigBuilder addProperty(String name, String value) {
     allProperties.put(NamingConvention.DOT.normalize(name), value);
     return this;
   }
 
+  /**
+   * Adds all properties from the passed {@code properties} to the config.
+   *
+   * @deprecated Use {@link #addProperties(Properties)} instead.
+   */
+  @Deprecated
   public ConfigBuilder readProperties(Properties properties) {
+    return addProperties(properties);
+  }
+
+  /**
+   * Adds all properties from the passed {@code properties} to the config.
+   *
+   * @deprecated Use {@link #addProperties(Map)} instead.
+   */
+  @Deprecated
+  public ConfigBuilder readProperties(Map<String, String> properties) {
+    return addProperties(properties);
+  }
+
+  /** Adds all properties from the passed {@code properties} to the config. */
+  public ConfigBuilder addProperties(Properties properties) {
     for (String name : properties.stringPropertyNames()) {
       allProperties.put(NamingConvention.DOT.normalize(name), properties.getProperty(name));
     }
     return this;
   }
 
-  public ConfigBuilder readProperties(Map<String, String> properties) {
+  /** Adds all properties from the passed {@code properties} to the config. */
+  public ConfigBuilder addProperties(Map<String, String> properties) {
     return fromConfigMap(properties, NamingConvention.DOT);
   }
 
-  /** Sets the configuration values from environment variables. */
+  /**
+   * Adds environment variables, converted to Java property naming convention, to the config.
+   *
+   * <p>Environment variable names are lowercased, and the underscores are replaced by dots.
+   *
+   * @deprecated Use {@link #addEnvironmentVariables()} instead.
+   */
+  @Deprecated
   public ConfigBuilder readEnvironmentVariables() {
+    return addEnvironmentVariables();
+  }
+
+  /**
+   * Adds environment variables, converted to Java property naming convention, to the config.
+   *
+   * <p>Environment variable names are lowercased, and the underscores are replaced by dots.
+   */
+  public ConfigBuilder addEnvironmentVariables() {
     return fromConfigMap(System.getenv(), NamingConvention.ENV_VAR);
   }
 
-  /** Sets the configuration values from system properties. */
+  /**
+   * Adds system properties to the config.
+   *
+   * @deprecated Use {@link #addSystemProperties()} instead.
+   */
+  @Deprecated
   public ConfigBuilder readSystemProperties() {
-    return readProperties(System.getProperties());
+    return addSystemProperties();
+  }
+
+  /** Adds system properties to the config. */
+  public ConfigBuilder addSystemProperties() {
+    return addProperties(System.getProperties());
   }
 
   private ConfigBuilder fromConfigMap(
@@ -48,6 +107,7 @@ public final class ConfigBuilder {
     return this;
   }
 
+  /** Returns a new {@link Config} with properties from this {@linkplain ConfigBuilder builder}. */
   public Config build() {
     return Config.create(Collections.unmodifiableMap(new HashMap<>(allProperties)));
   }

+ 12 - 8
instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/ContextPropagationDebug.java

@@ -29,14 +29,18 @@ public final class ContextPropagationDebug {
   private static final ContextKey<ContextPropagationDebug> THREAD_PROPAGATION_LOCATIONS =
       ContextKey.named("thread-propagation-locations");
 
-  private static final boolean THREAD_PROPAGATION_DEBUGGER =
-      Config.get()
-          .getBoolean(
-              "otel.javaagent.experimental.thread-propagation-debugger.enabled",
-              Config.get().isAgentDebugEnabled());
-
-  private static final boolean FAIL_ON_CONTEXT_LEAK =
-      Config.get().getBoolean("otel.javaagent.testing.fail-on-context-leak", false);
+  private static final boolean THREAD_PROPAGATION_DEBUGGER;
+  private static final boolean FAIL_ON_CONTEXT_LEAK;
+
+  static {
+    Config config = Config.get();
+    boolean agentDebugEnabled = config.getBoolean("otel.javaagent.debug", false);
+
+    THREAD_PROPAGATION_DEBUGGER =
+        config.getBoolean(
+            "otel.javaagent.experimental.thread-propagation-debugger.enabled", agentDebugEnabled);
+    FAIL_ON_CONTEXT_LEAK = config.getBoolean("otel.javaagent.testing.fail-on-context-leak", false);
+  }
 
   // context to which debug locations were added
   private final Context sourceContext;

+ 1 - 1
instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/SupportabilityMetrics.java

@@ -36,7 +36,7 @@ public final class SupportabilityMetrics {
 
   // visible for testing
   SupportabilityMetrics(Config config, Consumer<String> reporter) {
-    agentDebugEnabled = config.isAgentDebugEnabled();
+    agentDebugEnabled = config.getBoolean("otel.javaagent.debug", false);
     this.reporter = reporter;
   }
 

+ 0 - 63
instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/config/ConfigTest.java

@@ -6,7 +6,6 @@
 package io.opentelemetry.instrumentation.api.config;
 
 import static java.util.Arrays.asList;
-import static java.util.Collections.emptyList;
 import static java.util.Collections.emptyMap;
 import static java.util.Collections.singletonList;
 import static java.util.Collections.singletonMap;
@@ -18,15 +17,7 @@ import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.time.Duration;
-import java.util.List;
-import java.util.TreeSet;
-import java.util.stream.Stream;
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtensionContext;
-import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.Arguments;
-import org.junit.jupiter.params.provider.ArgumentsProvider;
-import org.junit.jupiter.params.provider.ArgumentsSource;
 
 // suppress duration unit check, e.g. ofMillis(5000) -> ofSeconds(5)
 @SuppressWarnings({"CanonicalDuration"})
@@ -140,58 +131,4 @@ class ConfigTest {
         .containsOnly(entry("three", "3"));
     assertThat(config.getMap("prop.trailing", emptyMap())).containsOnly(entry("one", "1"));
   }
-
-  @ParameterizedTest
-  @ArgumentsSource(AgentDebugParams.class)
-  void shouldCheckIfAgentDebugModeIsEnabled(String propertyValue, boolean expected) {
-    Config config = Config.builder().addProperty("otel.javaagent.debug", propertyValue).build();
-
-    assertEquals(expected, config.isAgentDebugEnabled());
-  }
-
-  private static class AgentDebugParams implements ArgumentsProvider {
-    @Override
-    public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
-      return Stream.of(
-          Arguments.of("true", true), Arguments.of("blather", false), Arguments.of(null, false));
-    }
-  }
-
-  @ParameterizedTest
-  @ArgumentsSource(InstrumentationEnabledParams.class)
-  void shouldCheckIfInstrumentationIsEnabled(
-      List<String> names, boolean defaultEnabled, boolean expected) {
-    Config config =
-        Config.builder()
-            .addProperty("otel.instrumentation.order.enabled", "true")
-            .addProperty("otel.instrumentation.test-prop.enabled", "true")
-            .addProperty("otel.instrumentation.disabled-prop.enabled", "false")
-            .addProperty("otel.instrumentation.test-env.enabled", "true")
-            .addProperty("otel.instrumentation.disabled-env.enabled", "false")
-            .build();
-
-    assertEquals(expected, config.isInstrumentationEnabled(new TreeSet<>(names), defaultEnabled));
-  }
-
-  private static class InstrumentationEnabledParams implements ArgumentsProvider {
-    @Override
-    public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
-      return Stream.of(
-          Arguments.of(emptyList(), true, true),
-          Arguments.of(emptyList(), false, false),
-          Arguments.of(singletonList("invalid"), true, true),
-          Arguments.of(singletonList("invalid"), false, false),
-          Arguments.of(singletonList("test-prop"), false, true),
-          Arguments.of(singletonList("test-env"), false, true),
-          Arguments.of(singletonList("disabled-prop"), true, false),
-          Arguments.of(singletonList("disabled-env"), true, false),
-          Arguments.of(asList("other", "test-prop"), false, true),
-          Arguments.of(asList("other", "test-env"), false, true),
-          Arguments.of(singletonList("order"), false, true),
-          Arguments.of(asList("test-prop", "disabled-prop"), false, true),
-          Arguments.of(asList("disabled-env", "test-env"), false, true),
-          Arguments.of(asList("test-prop", "disabled-prop"), true, false),
-          Arguments.of(asList("disabled-env", "test-env"), true, false));
-    }
-  }
 }

+ 1 - 4
instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/internal/SupportabilityMetricsTest.java

@@ -10,7 +10,6 @@ import static org.assertj.core.api.Assertions.assertThat;
 import io.opentelemetry.api.trace.SpanKind;
 import io.opentelemetry.instrumentation.api.config.Config;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 import org.junit.jupiter.api.Test;
 
@@ -78,8 +77,6 @@ class SupportabilityMetricsTest {
   }
 
   private static Config configWithJavaagentDebug(boolean enabled) {
-    return Config.builder()
-        .readProperties(Collections.singletonMap("otel.javaagent.debug", Boolean.toString(enabled)))
-        .build();
+    return Config.builder().addProperty("otel.javaagent.debug", Boolean.toString(enabled)).build();
   }
 }

+ 2 - 3
instrumentation/external-annotations/javaagent-unit-tests/src/test/groovy/IncludeTest.groovy

@@ -4,7 +4,6 @@
  */
 
 import io.opentelemetry.instrumentation.api.config.Config
-import io.opentelemetry.instrumentation.api.config.ConfigBuilder
 import io.opentelemetry.javaagent.instrumentation.extannotations.ExternalAnnotationInstrumentation
 import spock.lang.Specification
 import spock.lang.Unroll
@@ -18,11 +17,11 @@ class IncludeTest extends Specification {
     setup:
     Config config
     if (value) {
-      config = new ConfigBuilder().readProperties([
+      config = Config.builder().addProperties([
         "otel.instrumentation.external-annotations.include": value
       ]).build()
     } else {
-      config = Config.create([:])
+      config = Config.builder().build()
     }
 
     expect:

+ 1 - 1
instrumentation/grizzly-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grizzly/GrizzlyInstrumentationModule.java

@@ -29,7 +29,7 @@ public class GrizzlyInstrumentationModule extends InstrumentationModule {
   }
 
   @Override
-  protected boolean defaultEnabled() {
+  public boolean defaultEnabled() {
     return false;
   }
 }

+ 1 - 0
instrumentation/oshi/javaagent/build.gradle.kts

@@ -16,6 +16,7 @@ dependencies {
   implementation(project(":instrumentation:oshi:library"))
 
   compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")
+  compileOnly(project(":javaagent-tooling"))
 
   library("com.github.oshi:oshi-core:5.3.1")
 

+ 3 - 2
instrumentation/oshi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/oshi/OshiMetricsInstaller.java

@@ -8,6 +8,7 @@ package io.opentelemetry.javaagent.instrumentation.oshi;
 import com.google.auto.service.AutoService;
 import io.opentelemetry.instrumentation.api.config.Config;
 import io.opentelemetry.javaagent.extension.AgentListener;
+import io.opentelemetry.javaagent.tooling.config.AgentConfig;
 import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
 import java.lang.reflect.Method;
 import java.util.Collections;
@@ -20,8 +21,8 @@ import java.util.Collections;
 public class OshiMetricsInstaller implements AgentListener {
   @Override
   public void afterAgent(Config config, AutoConfiguredOpenTelemetrySdk unused) {
-    if (config.isInstrumentationEnabled(
-        Collections.singleton("oshi"), /* defaultEnabled= */ true)) {
+    if (new AgentConfig(config)
+        .isInstrumentationEnabled(Collections.singleton("oshi"), /* defaultEnabled= */ true)) {
       try {
         // Call oshi.SystemInfo.getCurrentPlatformEnum() to activate SystemMetrics.
         // Oshi instrumentation will intercept this call and enable SystemMetrics.

+ 1 - 0
instrumentation/runtime-metrics/javaagent/build.gradle.kts

@@ -6,4 +6,5 @@ dependencies {
   implementation(project(":instrumentation:runtime-metrics:library"))
 
   compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")
+  compileOnly(project(":javaagent-tooling"))
 }

+ 4 - 2
instrumentation/runtime-metrics/javaagent/src/main/java/io/opentelemetry/instrumentation/javaagent/runtimemetrics/RuntimeMetricsInstaller.java

@@ -11,6 +11,7 @@ import io.opentelemetry.instrumentation.api.config.Config;
 import io.opentelemetry.instrumentation.runtimemetrics.GarbageCollector;
 import io.opentelemetry.instrumentation.runtimemetrics.MemoryPools;
 import io.opentelemetry.javaagent.extension.AgentListener;
+import io.opentelemetry.javaagent.tooling.config.AgentConfig;
 import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
 import java.util.Collections;
 
@@ -19,8 +20,9 @@ import java.util.Collections;
 public class RuntimeMetricsInstaller implements AgentListener {
   @Override
   public void afterAgent(Config config, AutoConfiguredOpenTelemetrySdk unused) {
-    if (config.isInstrumentationEnabled(
-        Collections.singleton("runtime-metrics"), /* defaultEnabled= */ true)) {
+    if (new AgentConfig(config)
+        .isInstrumentationEnabled(
+            Collections.singleton("runtime-metrics"), /* defaultEnabled= */ true)) {
       GarbageCollector.registerObservers(GlobalOpenTelemetry.get());
       MemoryPools.registerObservers(GlobalOpenTelemetry.get());
     }

+ 2 - 16
instrumentation/spring/spring-batch-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/batch/SpringBatchInstrumentationConfig.java

@@ -5,37 +5,23 @@
 
 package io.opentelemetry.javaagent.instrumentation.spring.batch;
 
-import static java.util.Arrays.asList;
-import static java.util.Collections.unmodifiableList;
-
 import io.opentelemetry.instrumentation.api.config.Config;
-import java.util.List;
 
 public final class SpringBatchInstrumentationConfig {
 
   private static final String INSTRUMENTATION_NAME = "io.opentelemetry.spring-batch-3.0";
 
-  private static final List<String> INSTRUMENTATION_NAMES =
-      unmodifiableList(asList("spring-batch", "spring-batch-3.0"));
-
   // the item level instrumentation is very chatty so it's disabled by default
   private static final boolean ITEM_TRACING_ENABLED =
-      Config.get()
-          .isInstrumentationPropertyEnabled(
-              instrumentationNames(), "item.enabled", /* defaultEnabled= */ false);
+      Config.get().getBoolean("otel.instrumentation.spring-batch.item.enabled", false);
   private static final boolean CREATE_ROOT_SPAN_FOR_CHUNK =
       Config.get()
-          .isInstrumentationPropertyEnabled(
-              instrumentationNames(), "experimental.chunk.new-trace", /* defaultEnabled= */ false);
+          .getBoolean("otel.instrumentation.spring-batch.experimental.chunk.new-trace", false);
 
   public static String instrumentationName() {
     return INSTRUMENTATION_NAME;
   }
 
-  public static List<String> instrumentationNames() {
-    return INSTRUMENTATION_NAMES;
-  }
-
   public static boolean shouldTraceItems() {
     return ITEM_TRACING_ENABLED;
   }

+ 2 - 3
instrumentation/spring/spring-batch-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/batch/SpringBatchInstrumentationModule.java

@@ -6,7 +6,6 @@
 package io.opentelemetry.javaagent.instrumentation.spring.batch;
 
 import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
-import static io.opentelemetry.javaagent.instrumentation.spring.batch.SpringBatchInstrumentationConfig.instrumentationNames;
 
 import com.google.auto.service.AutoService;
 import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
@@ -27,7 +26,7 @@ import net.bytebuddy.matcher.ElementMatcher;
 @AutoService(InstrumentationModule.class)
 public class SpringBatchInstrumentationModule extends InstrumentationModule {
   public SpringBatchInstrumentationModule() {
-    super(instrumentationNames());
+    super("spring-batch", "spring-batch-3.0");
   }
 
   @Override
@@ -55,7 +54,7 @@ public class SpringBatchInstrumentationModule extends InstrumentationModule {
   }
 
   @Override
-  protected boolean defaultEnabled() {
+  public boolean defaultEnabled() {
     // TODO: replace this with an experimental flag
     return false;
   }

+ 20 - 9
javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/instrumentation/InstrumentationModule.java

@@ -6,11 +6,11 @@
 package io.opentelemetry.javaagent.extension.instrumentation;
 
 import static java.util.Arrays.asList;
+import static java.util.Collections.unmodifiableSet;
 import static net.bytebuddy.matcher.ElementMatchers.any;
 
 import io.opentelemetry.instrumentation.api.config.Config;
 import io.opentelemetry.javaagent.extension.Ordered;
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.LinkedHashSet;
 import java.util.List;
@@ -57,25 +57,31 @@ public abstract class InstrumentationModule implements Ordered {
    */
   protected InstrumentationModule(
       String mainInstrumentationName, String... additionalInstrumentationNames) {
-    this(toList(mainInstrumentationName, additionalInstrumentationNames));
+    LinkedHashSet<String> names = new LinkedHashSet<>(additionalInstrumentationNames.length + 1);
+    names.add(mainInstrumentationName);
+    names.addAll(asList(additionalInstrumentationNames));
+    this.instrumentationNames = unmodifiableSet(names);
   }
 
   /**
    * Creates an instrumentation module.
    *
    * @see #InstrumentationModule(String, String...)
+   * @deprecated Use {@link #InstrumentationModule(String, String...)} instead.
    */
+  @Deprecated
   protected InstrumentationModule(List<String> instrumentationNames) {
     if (instrumentationNames.isEmpty()) {
       throw new IllegalArgumentException("InstrumentationModules must be named");
     }
-    this.instrumentationNames = new LinkedHashSet<>(instrumentationNames);
+    this.instrumentationNames = unmodifiableSet(new LinkedHashSet<>(instrumentationNames));
   }
 
-  private static List<String> toList(String first, String[] rest) {
-    List<String> instrumentationNames = new ArrayList<>(rest.length + 1);
-    instrumentationNames.add(first);
-    instrumentationNames.addAll(asList(rest));
+  /**
+   * Returns all instrumentation names assigned to this module. See {@link
+   * #InstrumentationModule(String, String...)} for more details about instrumentation names.
+   */
+  public final Set<String> instrumentationNames() {
     return instrumentationNames;
   }
 
@@ -87,7 +93,12 @@ public abstract class InstrumentationModule implements Ordered {
     return instrumentationNames.iterator().next();
   }
 
-  /** Returns true if this instrumentation module should be installed. */
+  /**
+   * Returns true if this instrumentation module should be installed.
+   *
+   * @deprecated This method will be removed.
+   */
+  @Deprecated
   public final boolean isEnabled() {
     return Config.get().isInstrumentationEnabled(instrumentationNames, defaultEnabled());
   }
@@ -96,7 +107,7 @@ public abstract class InstrumentationModule implements Ordered {
    * Allows instrumentation modules to disable themselves by default, or to additionally disable
    * themselves on some other condition.
    */
-  protected boolean defaultEnabled() {
+  public boolean defaultEnabled() {
     return DEFAULT_ENABLED;
   }
 

+ 0 - 74
javaagent-extension-api/src/test/groovy/io/opentelemetry/javaagent/extension/instrumentation/InstrumentationModuleTest.groovy

@@ -1,74 +0,0 @@
-/*
- * Copyright The OpenTelemetry Authors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package io.opentelemetry.javaagent.extension.instrumentation
-
-import io.opentelemetry.instrumentation.api.config.Config
-import io.opentelemetry.instrumentation.api.config.ConfigBuilder
-import spock.lang.Specification
-
-class InstrumentationModuleTest extends Specification {
-
-  def "default enabled"() {
-    setup:
-    def target = new TestInstrumentationModule(["test"])
-
-    expect:
-    target.enabled
-  }
-
-  def "default enabled override"() {
-    expect:
-    target.enabled == enabled
-
-    where:
-    enabled | target
-    true    | new TestInstrumentationModule(["test"]) {
-      @Override
-      protected boolean defaultEnabled() {
-        return true
-      }
-    }
-    false   | new TestInstrumentationModule(["test"]) {
-      @Override
-      protected boolean defaultEnabled() {
-        return false
-      }
-    }
-  }
-
-  def "default disabled can override to enabled #enabled"() {
-    setup:
-    Config.instance = new ConfigBuilder().readProperties([
-      "otel.instrumentation.test.enabled": Boolean.toString(enabled)
-    ]).build()
-    def target = new TestInstrumentationModule(["test"]) {
-      @Override
-      protected boolean defaultEnabled() {
-        return false
-      }
-    }
-
-    expect:
-    target.enabled == enabled
-
-    cleanup:
-    Config.instance = null
-
-    where:
-    enabled << [true, false]
-  }
-
-  static class TestInstrumentationModule extends InstrumentationModule {
-    TestInstrumentationModule(List<String> instrumentationNames) {
-      super(instrumentationNames)
-    }
-
-    @Override
-    List<TypeInstrumentation> typeInstrumentations() {
-      return []
-    }
-  }
-}

+ 2 - 1
javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentInstaller.java

@@ -31,6 +31,7 @@ import io.opentelemetry.javaagent.instrumentation.api.internal.InstrumentedTaskC
 import io.opentelemetry.javaagent.tooling.asyncannotationsupport.WeakRefAsyncOperationEndStrategies;
 import io.opentelemetry.javaagent.tooling.bootstrap.BootstrapPackagesBuilderImpl;
 import io.opentelemetry.javaagent.tooling.bootstrap.BootstrapPackagesConfigurer;
+import io.opentelemetry.javaagent.tooling.config.AgentConfig;
 import io.opentelemetry.javaagent.tooling.config.ConfigInitializer;
 import io.opentelemetry.javaagent.tooling.ignore.IgnoredClassLoadersMatcher;
 import io.opentelemetry.javaagent.tooling.ignore.IgnoredTypesBuilderImpl;
@@ -149,7 +150,7 @@ public class AgentInstaller {
 
     agentBuilder = configureIgnoredTypes(config, agentBuilder);
 
-    if (config.isAgentDebugEnabled()) {
+    if (AgentConfig.get().isDebugModeEnabled()) {
       agentBuilder =
           agentBuilder
               .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)

+ 2 - 1
javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentTracerProviderConfigurer.java

@@ -10,6 +10,7 @@ import static io.opentelemetry.javaagent.tooling.AgentInstaller.JAVAAGENT_ENABLE
 import com.google.auto.service.AutoService;
 import io.opentelemetry.exporter.logging.LoggingSpanExporter;
 import io.opentelemetry.instrumentation.api.config.Config;
+import io.opentelemetry.javaagent.tooling.config.AgentConfig;
 import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer;
 import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider;
 import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
@@ -43,7 +44,7 @@ public class AgentTracerProviderConfigurer implements AutoConfigurationCustomize
   }
 
   private static void maybeEnableLoggingExporter(SdkTracerProviderBuilder builder) {
-    if (Config.get().isAgentDebugEnabled()) {
+    if (AgentConfig.get().isDebugModeEnabled()) {
       // don't install another instance if the user has already explicitly requested it.
       if (loggingExporterIsNotAlreadyConfigured()) {
         builder.addSpanProcessor(SimpleSpanProcessor.create(LoggingSpanExporter.create()));

+ 45 - 0
javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/config/AgentConfig.java

@@ -0,0 +1,45 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.tooling.config;
+
+import io.opentelemetry.instrumentation.api.config.Config;
+
+public final class AgentConfig {
+
+  private static final AgentConfig instance = new AgentConfig(Config.get());
+
+  private final Config config;
+
+  public static AgentConfig get() {
+    return instance;
+  }
+
+  public AgentConfig(Config config) {
+    this.config = config;
+  }
+
+  public boolean isInstrumentationEnabled(
+      Iterable<String> instrumentationNames, boolean defaultEnabled) {
+    // If default is enabled, we want to enable individually,
+    // if default is disabled, we want to disable individually.
+    boolean anyEnabled = defaultEnabled;
+    for (String name : instrumentationNames) {
+      String propertyName = "otel.instrumentation." + name + ".enabled";
+      boolean enabled = config.getBoolean(propertyName, defaultEnabled);
+
+      if (defaultEnabled) {
+        anyEnabled &= enabled;
+      } else {
+        anyEnabled |= enabled;
+      }
+    }
+    return anyEnabled;
+  }
+
+  public boolean isDebugModeEnabled() {
+    return config.getBoolean("otel.javaagent.debug", false);
+  }
+}

+ 4 - 4
javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/config/ConfigInitializer.java

@@ -33,10 +33,10 @@ public final class ConfigInitializer {
   // visible for testing
   static Config create(Properties spiConfiguration, Properties configurationFile) {
     return Config.builder()
-        .readProperties(spiConfiguration)
-        .readProperties(configurationFile)
-        .readEnvironmentVariables()
-        .readSystemProperties()
+        .addProperties(spiConfiguration)
+        .addProperties(configurationFile)
+        .addEnvironmentVariables()
+        .addSystemProperties()
         .build();
   }
 

+ 4 - 1
javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/instrumentation/InstrumentationModuleInstaller.java

@@ -19,6 +19,7 @@ import io.opentelemetry.javaagent.tooling.HelperInjector;
 import io.opentelemetry.javaagent.tooling.TransformSafeLogger;
 import io.opentelemetry.javaagent.tooling.Utils;
 import io.opentelemetry.javaagent.tooling.bytebuddy.LoggingFailSafeMatcher;
+import io.opentelemetry.javaagent.tooling.config.AgentConfig;
 import io.opentelemetry.javaagent.tooling.field.VirtualFieldImplementationInstaller;
 import io.opentelemetry.javaagent.tooling.field.VirtualFieldImplementationInstallerFactory;
 import io.opentelemetry.javaagent.tooling.muzzle.HelperResourceBuilderImpl;
@@ -56,7 +57,9 @@ public final class InstrumentationModuleInstaller {
 
   AgentBuilder install(
       InstrumentationModule instrumentationModule, AgentBuilder parentAgentBuilder) {
-    if (!instrumentationModule.isEnabled()) {
+    if (!AgentConfig.get()
+        .isInstrumentationEnabled(
+            instrumentationModule.instrumentationNames(), instrumentationModule.defaultEnabled())) {
       logger.log(
           FINE, "Instrumentation {0} is disabled", instrumentationModule.instrumentationName());
       return parentAgentBuilder;

+ 81 - 0
javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/config/AgentConfigTest.java

@@ -0,0 +1,81 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.tooling.config;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.emptyList;
+import static java.util.Collections.singletonList;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import io.opentelemetry.instrumentation.api.config.Config;
+import java.util.List;
+import java.util.TreeSet;
+import java.util.stream.Stream;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.ArgumentsProvider;
+import org.junit.jupiter.params.provider.ArgumentsSource;
+
+class AgentConfigTest {
+
+  @ParameterizedTest
+  @ArgumentsSource(AgentDebugParams.class)
+  void shouldCheckIfAgentDebugModeIsEnabled(String propertyValue, boolean expected) {
+    Config config = Config.builder().addProperty("otel.javaagent.debug", propertyValue).build();
+    AgentConfig agentConfig = new AgentConfig(config);
+
+    assertEquals(expected, agentConfig.isDebugModeEnabled());
+  }
+
+  private static class AgentDebugParams implements ArgumentsProvider {
+    @Override
+    public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
+      return Stream.of(
+          Arguments.of("true", true), Arguments.of("blather", false), Arguments.of(null, false));
+    }
+  }
+
+  @ParameterizedTest
+  @ArgumentsSource(InstrumentationEnabledParams.class)
+  void shouldCheckIfInstrumentationIsEnabled(
+      List<String> names, boolean defaultEnabled, boolean expected) {
+    Config config =
+        Config.builder()
+            .addProperty("otel.instrumentation.order.enabled", "true")
+            .addProperty("otel.instrumentation.test-prop.enabled", "true")
+            .addProperty("otel.instrumentation.disabled-prop.enabled", "false")
+            .addProperty("otel.instrumentation.test-env.enabled", "true")
+            .addProperty("otel.instrumentation.disabled-env.enabled", "false")
+            .build();
+    AgentConfig agentConfig = new AgentConfig(config);
+
+    assertEquals(
+        expected, agentConfig.isInstrumentationEnabled(new TreeSet<>(names), defaultEnabled));
+  }
+
+  private static class InstrumentationEnabledParams implements ArgumentsProvider {
+    @Override
+    public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
+      return Stream.of(
+          Arguments.of(emptyList(), true, true),
+          Arguments.of(emptyList(), false, false),
+          Arguments.of(singletonList("invalid"), true, true),
+          Arguments.of(singletonList("invalid"), false, false),
+          Arguments.of(singletonList("test-prop"), false, true),
+          Arguments.of(singletonList("test-env"), false, true),
+          Arguments.of(singletonList("disabled-prop"), true, false),
+          Arguments.of(singletonList("disabled-env"), true, false),
+          Arguments.of(asList("other", "test-prop"), false, true),
+          Arguments.of(asList("other", "test-env"), false, true),
+          Arguments.of(singletonList("order"), false, true),
+          Arguments.of(asList("test-prop", "disabled-prop"), false, true),
+          Arguments.of(asList("disabled-env", "test-env"), false, true),
+          Arguments.of(asList("test-prop", "disabled-prop"), true, false),
+          Arguments.of(asList("disabled-env", "test-env"), true, false));
+    }
+  }
+}

+ 1 - 1
javaagent-tooling/src/test/java/io/opentelemetry/javaagent/tooling/ignore/UserExcludedClassesConfigurerTest.java

@@ -38,7 +38,7 @@ class UserExcludedClassesConfigurerTest {
     // given
     Config config =
         Config.builder()
-            .readProperties(
+            .addProperties(
                 singletonMap(
                     EXCLUDED_CLASSES_CONFIG,
                     "com.example.IgnoredClass,com.example.ignored.*,com.another_ignore"))