From c0a21530cb43d6d2e062a7e1dbb00e59d51c5645 Mon Sep 17 00:00:00 2001 From: Mateusz Rzeszutek Date: Mon, 16 May 2022 20:55:01 +0200 Subject: [PATCH] Add an SPI for customizing Config just before it's set (#6010) * Add an SPI for customizing Config just before it's set * deprecate ConfigPropertySource in favor of ConfigCustomizer * errorprone --- .../instrumentation/api/config/Config.java | 8 ++++ .../api/config/ConfigBuilder.java | 11 ++++-- .../extension/config/ConfigCustomizer.java | 38 +++++++++++++++++++ .../config/ConfigPropertySource.java | 7 ++++ .../tooling/config/ConfigInitializer.java | 19 ++++++++-- .../AgentTestingExporterPropertySource.java | 9 +++-- .../CapturedHttpHeadersTestConfigSource.java | 8 ++-- 7 files changed, 85 insertions(+), 15 deletions(-) create mode 100644 javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/config/ConfigCustomizer.java diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/config/Config.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/config/Config.java index 340525471952..33bda37ddd72 100644 --- a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/config/Config.java +++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/config/Config.java @@ -192,4 +192,12 @@ private T getTypedProperty(String name, ConfigValueParser parser) { private String getRawProperty(String name, String defaultValue) { return getAllProperties().getOrDefault(NamingConvention.DOT.normalize(name), defaultValue); } + + /** + * Returns a new {@link ConfigBuilder} instance populated with the properties of this {@link + * Config}. + */ + public ConfigBuilder toBuilder() { + return new ConfigBuilder(getAllProperties()); + } } diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/config/ConfigBuilder.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/config/ConfigBuilder.java index a2f7bd4c3611..d291a9646f9c 100644 --- a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/config/ConfigBuilder.java +++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/config/ConfigBuilder.java @@ -14,10 +14,15 @@ /** A builder of a {@link Config}. */ public final class ConfigBuilder { - private final Map allProperties = new HashMap<>(); + private final Map allProperties; - /** Constructs a new {@link ConfigBuilder}. */ - ConfigBuilder() {} + ConfigBuilder() { + allProperties = new HashMap<>(); + } + + ConfigBuilder(Map propertiesToCopy) { + allProperties = new HashMap<>(propertiesToCopy); + } /** Adds a single property to the config. */ public ConfigBuilder addProperty(String name, @Nullable String value) { diff --git a/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/config/ConfigCustomizer.java b/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/config/ConfigCustomizer.java new file mode 100644 index 000000000000..2abb507d8cf4 --- /dev/null +++ b/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/config/ConfigCustomizer.java @@ -0,0 +1,38 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.extension.config; + +import io.opentelemetry.instrumentation.api.config.Config; +import io.opentelemetry.javaagent.extension.Ordered; +import java.util.Collections; +import java.util.Map; + +/** + * A service provider that allows to override default OTel javaagent configuration, and customize + * the config just before it is set as the global. + * + *

This is a service provider interface that requires implementations to be registered in a + * provider-configuration file stored in the {@code META-INF/services} resource directory. + */ +public interface ConfigCustomizer extends Ordered { + + /** + * Returns properties with their default values. Properties returned by implementations of this + * interface will be used after the following methods fail to find a non-empty property value: + * system properties, environment variables, properties configuration file. + * + *

Key of the map is the propertyName (same as system property name, e.g. {@code + * otel.traces.exporter}), value is the property value. + */ + default Map defaultProperties() { + return Collections.emptyMap(); + } + + /** Allows to change the javaagent configuration just before it is first used. */ + default Config customize(Config config) { + return config; + } +} diff --git a/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/config/ConfigPropertySource.java b/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/config/ConfigPropertySource.java index 75f2ae20c2db..79c4d4375027 100644 --- a/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/config/ConfigPropertySource.java +++ b/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/config/ConfigPropertySource.java @@ -16,12 +16,19 @@ * *

This is a service provider interface that requires implementations to be registered in a * provider-configuration file stored in the {@code META-INF/services} resource directory. + * + * @deprecated Use {@link ConfigCustomizer} instead. */ +@Deprecated public interface ConfigPropertySource extends Ordered { + /** * Returns all properties whose default values are overridden by this property source. Key of the * map is the propertyName (same as system property name, e.g. {@code otel.traces.exporter}), * value is the property value. + * + * @deprecated Use {@link ConfigCustomizer#defaultProperties()} instead. */ + @Deprecated Map getProperties(); } diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/config/ConfigInitializer.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/config/ConfigInitializer.java index 581d2aaba482..4c6e4789ebd9 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/config/ConfigInitializer.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/config/ConfigInitializer.java @@ -9,13 +9,14 @@ import static java.util.logging.Level.SEVERE; import io.opentelemetry.instrumentation.api.config.Config; -import io.opentelemetry.javaagent.extension.config.ConfigPropertySource; +import io.opentelemetry.javaagent.extension.config.ConfigCustomizer; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; +import java.util.List; import java.util.Properties; import java.util.logging.Logger; @@ -27,7 +28,12 @@ public final class ConfigInitializer { static final String CONFIGURATION_FILE_ENV_VAR = "OTEL_JAVAAGENT_CONFIGURATION_FILE"; public static void initialize() { - Config.internalInitializeConfig(create(loadSpiConfiguration(), loadConfigurationFile())); + List customizers = loadOrdered(ConfigCustomizer.class); + Config config = create(loadSpiConfiguration(customizers), loadConfigurationFile()); + for (ConfigCustomizer customizer : customizers) { + config = customizer.customize(config); + } + Config.internalInitializeConfig(config); } // visible for testing @@ -41,11 +47,16 @@ static Config create(Properties spiConfiguration, Properties configurationFile) } /** Retrieves all default configuration overloads using SPI and initializes Config. */ - private static Properties loadSpiConfiguration() { + @SuppressWarnings("deprecation") // loads the old config SPI + private static Properties loadSpiConfiguration(List customizers) { Properties propertiesFromSpi = new Properties(); - for (ConfigPropertySource propertySource : loadOrdered(ConfigPropertySource.class)) { + for (io.opentelemetry.javaagent.extension.config.ConfigPropertySource propertySource : + loadOrdered(io.opentelemetry.javaagent.extension.config.ConfigPropertySource.class)) { propertiesFromSpi.putAll(propertySource.getProperties()); } + for (ConfigCustomizer customizer : customizers) { + propertiesFromSpi.putAll(customizer.defaultProperties()); + } return propertiesFromSpi; } diff --git a/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/exporter/AgentTestingExporterPropertySource.java b/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/exporter/AgentTestingExporterPropertySource.java index ee5da95a5b2c..e4f85e6de461 100644 --- a/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/exporter/AgentTestingExporterPropertySource.java +++ b/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/exporter/AgentTestingExporterPropertySource.java @@ -6,14 +6,15 @@ package io.opentelemetry.javaagent.testing.exporter; import com.google.auto.service.AutoService; -import io.opentelemetry.javaagent.extension.config.ConfigPropertySource; +import io.opentelemetry.javaagent.extension.config.ConfigCustomizer; import java.util.HashMap; import java.util.Map; -@AutoService(ConfigPropertySource.class) -public class AgentTestingExporterPropertySource implements ConfigPropertySource { +@AutoService(ConfigCustomizer.class) +public class AgentTestingExporterPropertySource implements ConfigCustomizer { + @Override - public Map getProperties() { + public Map defaultProperties() { Map properties = new HashMap<>(); properties.put("otel.logs.exporter", "none"); properties.put("otel.metrics.exporter", "none"); diff --git a/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/http/CapturedHttpHeadersTestConfigSource.java b/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/http/CapturedHttpHeadersTestConfigSource.java index eec0215d5404..a00efdcb9cff 100644 --- a/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/http/CapturedHttpHeadersTestConfigSource.java +++ b/testing/agent-exporter/src/main/java/io/opentelemetry/javaagent/testing/http/CapturedHttpHeadersTestConfigSource.java @@ -6,15 +6,15 @@ package io.opentelemetry.javaagent.testing.http; import com.google.auto.service.AutoService; -import io.opentelemetry.javaagent.extension.config.ConfigPropertySource; +import io.opentelemetry.javaagent.extension.config.ConfigCustomizer; import java.util.HashMap; import java.util.Map; -@AutoService(ConfigPropertySource.class) -public class CapturedHttpHeadersTestConfigSource implements ConfigPropertySource { +@AutoService(ConfigCustomizer.class) +public class CapturedHttpHeadersTestConfigSource implements ConfigCustomizer { @Override - public Map getProperties() { + public Map defaultProperties() { Map testConfig = new HashMap<>(); testConfig.put("otel.instrumentation.http.capture-headers.client.request", "X-Test-Request"); testConfig.put("otel.instrumentation.http.capture-headers.client.response", "X-Test-Response");