diff --git a/declarative-config-bridge/src/main/java/io/opentelemetry/instrumentation/config/bridge/ConfigPropertiesBackedConfigProvider.java b/declarative-config-bridge/src/main/java/io/opentelemetry/instrumentation/config/bridge/ConfigPropertiesBackedConfigProvider.java new file mode 100644 index 000000000000..b35136987745 --- /dev/null +++ b/declarative-config-bridge/src/main/java/io/opentelemetry/instrumentation/config/bridge/ConfigPropertiesBackedConfigProvider.java @@ -0,0 +1,38 @@ +/* + * 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.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import javax.annotation.Nullable; + +/** + * A {@link ConfigProvider} implementation backed by {@link ConfigProperties}. + * + *

This allows instrumentations to always use {@code ExtendedOpenTelemetry.getConfigProvider()} + * regardless of whether the user started with system properties or YAML. + */ +public final class ConfigPropertiesBackedConfigProvider implements ConfigProvider { + + private final DeclarativeConfigProperties instrumentationConfig; + + public static ConfigProvider create(ConfigProperties configProperties) { + return new ConfigPropertiesBackedConfigProvider(configProperties); + } + + private ConfigPropertiesBackedConfigProvider(ConfigProperties configProperties) { + this.instrumentationConfig = + ConfigPropertiesBackedDeclarativeConfigProperties.createInstrumentationConfig( + configProperties); + } + + @Nullable + @Override + public DeclarativeConfigProperties getInstrumentationConfig() { + return instrumentationConfig; + } +} diff --git a/declarative-config-bridge/src/main/java/io/opentelemetry/instrumentation/config/bridge/ConfigPropertiesBackedDeclarativeConfigProperties.java b/declarative-config-bridge/src/main/java/io/opentelemetry/instrumentation/config/bridge/ConfigPropertiesBackedDeclarativeConfigProperties.java new file mode 100644 index 000000000000..5ab0cf14677a --- /dev/null +++ b/declarative-config-bridge/src/main/java/io/opentelemetry/instrumentation/config/bridge/ConfigPropertiesBackedDeclarativeConfigProperties.java @@ -0,0 +1,207 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.config.bridge; + +import static java.util.Collections.emptySet; + +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.common.ComponentLoader; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import javax.annotation.Nullable; + +/** + * Implementation of {@link DeclarativeConfigProperties} backed by {@link ConfigProperties}. + * + *

It tracks the navigation path and only resolves to system properties at the leaf node when a + * value is actually requested. + */ +public final class ConfigPropertiesBackedDeclarativeConfigProperties + implements DeclarativeConfigProperties { + + private static final String GENERAL_PEER_SERVICE_MAPPING = "general.peer.service_mapping"; + + private static final Map LIST_MAPPINGS; + + static { + LIST_MAPPINGS = new HashMap<>(); + LIST_MAPPINGS.put( + "general.http.client.request_captured_headers", + "otel.instrumentation.http.client.capture-request-headers"); + LIST_MAPPINGS.put( + "general.http.client.response_captured_headers", + "otel.instrumentation.http.client.capture-response-headers"); + LIST_MAPPINGS.put( + "general.http.server.request_captured_headers", + "otel.instrumentation.http.server.capture-request-headers"); + LIST_MAPPINGS.put( + "general.http.server.response_captured_headers", + "otel.instrumentation.http.server.capture-response-headers"); + } + + private final ConfigProperties configProperties; + private final List path; + + public static DeclarativeConfigProperties createInstrumentationConfig( + ConfigProperties configProperties) { + return new ConfigPropertiesBackedDeclarativeConfigProperties( + configProperties, Collections.emptyList()); + } + + private ConfigPropertiesBackedDeclarativeConfigProperties( + ConfigProperties configProperties, List path) { + this.configProperties = configProperties; + this.path = path; + } + + @Nullable + @Override + public String getString(String name) { + String fullPath = pathWithName(name); + return configProperties.getString(toPropertyKey(fullPath)); + } + + @Nullable + @Override + public Boolean getBoolean(String name) { + String fullPath = pathWithName(name); + return configProperties.getBoolean(toPropertyKey(fullPath)); + } + + @Nullable + @Override + public Integer getInt(String name) { + String fullPath = pathWithName(name); + return configProperties.getInt(toPropertyKey(fullPath)); + } + + @Nullable + @Override + public Long getLong(String name) { + String fullPath = pathWithName(name); + return configProperties.getLong(toPropertyKey(fullPath)); + } + + @Nullable + @Override + public Double getDouble(String name) { + String fullPath = pathWithName(name); + return configProperties.getDouble(toPropertyKey(fullPath)); + } + + /** + * Important: this method should return null if there is no structured child with the given name, + * but unfortunately that is not implementable on top of ConfigProperties. + * + *

This will be misleading if anyone is comparing the return value to null. + */ + @Override + public DeclarativeConfigProperties getStructured(String name) { + List newPath = new ArrayList<>(path); + newPath.add(name); + return new ConfigPropertiesBackedDeclarativeConfigProperties(configProperties, newPath); + } + + @Nullable + @Override + @SuppressWarnings("unchecked") // Safe because T is known to be String via scalarType check + public List getScalarList(String name, Class scalarType) { + if (scalarType != String.class) { + return null; + } + String fullPath = pathWithName(name); + + // Check explicit list mappings first + String mappedKey = LIST_MAPPINGS.get(fullPath); + if (mappedKey != null) { + List list = configProperties.getList(mappedKey); + if (!list.isEmpty()) { + return (List) list; + } + return null; + } + + // Standard mapping + List list = configProperties.getList(toPropertyKey(fullPath)); + if (list.isEmpty()) { + return null; + } + return (List) list; + } + + @Nullable + @Override + public List getStructuredList(String name) { + String fullPath = pathWithName(name); + if (GENERAL_PEER_SERVICE_MAPPING.equals(fullPath)) { + return PeerServiceMapping.getList(configProperties); + } + return null; + } + + @Override + public Set getPropertyKeys() { + // this is not supported when using system properties based configuration + return emptySet(); + } + + @Override + public ComponentLoader getComponentLoader() { + return configProperties.getComponentLoader(); + } + + private String pathWithName(String name) { + if (path.isEmpty()) { + return name; + } + return String.join(".", path) + "." + name; + } + + private static String toPropertyKey(String fullPath) { + String translatedPath = translatePath(fullPath); + + // Handle agent prefix: java.agent.* → otel.javaagent.* + if (translatedPath.startsWith("agent.")) { + return "otel.java" + translatedPath; + } + + // Handle jmx prefix: java.jmx.* → otel.jmx.* + if (translatedPath.startsWith("jmx.")) { + return "otel." + translatedPath; + } + + // Standard mapping + return "otel.instrumentation." + translatedPath; + } + + private static String translatePath(String path) { + StringBuilder result = new StringBuilder(); + for (String segment : path.split("\\.")) { + // Skip "java" segment - it doesn't exist in system properties + if ("java".equals(segment)) { + continue; + } + if (result.length() > 0) { + result.append("."); + } + result.append(translateName(segment)); + } + return result.toString(); + } + + private static String translateName(String name) { + if (name.endsWith("/development")) { + return "experimental." + + name.substring(0, name.length() - "/development".length()).replace('_', '-'); + } + return name.replace('_', '-'); + } +} diff --git a/declarative-config-bridge/src/main/java/io/opentelemetry/instrumentation/config/bridge/PeerServiceMapping.java b/declarative-config-bridge/src/main/java/io/opentelemetry/instrumentation/config/bridge/PeerServiceMapping.java new file mode 100644 index 000000000000..49e86576ce20 --- /dev/null +++ b/declarative-config-bridge/src/main/java/io/opentelemetry/instrumentation/config/bridge/PeerServiceMapping.java @@ -0,0 +1,105 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.config.bridge; + +import static java.util.Collections.emptySet; + +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.common.ComponentLoader; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import javax.annotation.Nullable; + +final class PeerServiceMapping implements DeclarativeConfigProperties { + + private final Map fields; + private final ComponentLoader componentLoader; + + @Nullable + static List getList(ConfigProperties configProperties) { + Map map = + configProperties.getMap("otel.instrumentation.common.peer-service-mapping"); + if (map.isEmpty()) { + return null; + } + List result = new ArrayList<>(); + for (Map.Entry entry : map.entrySet()) { + Map fields = new HashMap<>(); + fields.put("peer", entry.getKey()); + fields.put("service", entry.getValue()); + result.add(new PeerServiceMapping(fields, configProperties.getComponentLoader())); + } + return result; + } + + private PeerServiceMapping(Map fields, ComponentLoader componentLoader) { + this.fields = fields; + this.componentLoader = componentLoader; + } + + @Nullable + @Override + public String getString(String name) { + return fields.get(name); + } + + @Nullable + @Override + public Boolean getBoolean(String name) { + return null; + } + + @Nullable + @Override + public Integer getInt(String name) { + return null; + } + + @Nullable + @Override + public Long getLong(String name) { + return null; + } + + @Nullable + @Override + public Double getDouble(String name) { + return null; + } + + @Nullable + @Override + public DeclarativeConfigProperties getStructured(String name) { + return null; + } + + @Nullable + @Override + public List getScalarList(String name, Class scalarType) { + return null; + } + + @Nullable + @Override + public List getStructuredList(String name) { + return null; + } + + @Override + public Set getPropertyKeys() { + // this is not supported when using system properties based configuration + return emptySet(); + } + + @Override + public ComponentLoader getComponentLoader() { + return componentLoader; + } +} diff --git a/instrumentation/graphql-java/graphql-java-12.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/graphql/v12_0/GraphqlSingletons.java b/instrumentation/graphql-java/graphql-java-12.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/graphql/v12_0/GraphqlSingletons.java index 79e0130d3129..fe75237fa81f 100644 --- a/instrumentation/graphql-java/graphql-java-12.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/graphql/v12_0/GraphqlSingletons.java +++ b/instrumentation/graphql-java/graphql-java-12.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/graphql/v12_0/GraphqlSingletons.java @@ -5,36 +5,72 @@ package io.opentelemetry.javaagent.instrumentation.graphql.v12_0; +import static io.opentelemetry.api.incubator.config.DeclarativeConfigProperties.empty; + import graphql.execution.instrumentation.Instrumentation; import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.incubator.ExtendedOpenTelemetry; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; import io.opentelemetry.instrumentation.graphql.internal.InstrumentationUtil; import io.opentelemetry.instrumentation.graphql.v12_0.GraphQLTelemetry; -import io.opentelemetry.javaagent.bootstrap.internal.AgentInstrumentationConfig; public final class GraphqlSingletons { - private static final boolean CAPTURE_QUERY = - AgentInstrumentationConfig.get() - .getBoolean("otel.instrumentation.graphql.capture-query", true); - private static final boolean QUERY_SANITIZATION_ENABLED = - AgentInstrumentationConfig.get() - .getBoolean("otel.instrumentation.graphql.query-sanitizer.enabled", true); - private static final boolean ADD_OPERATION_NAME_TO_SPAN_NAME = - AgentInstrumentationConfig.get() - .getBoolean( - "otel.instrumentation.graphql.add-operation-name-to-span-name.enabled", false); - - private static final GraphQLTelemetry TELEMETRY = - GraphQLTelemetry.builder(GlobalOpenTelemetry.get()) - .setCaptureQuery(CAPTURE_QUERY) - .setSanitizeQuery(QUERY_SANITIZATION_ENABLED) - .setAddOperationNameToSpanName(ADD_OPERATION_NAME_TO_SPAN_NAME) - .build(); + private static final GraphQLTelemetry TELEMETRY; - private GraphqlSingletons() {} + static { + OpenTelemetry openTelemetry = GlobalOpenTelemetry.get(); + Configuration config = new Configuration(openTelemetry); + + TELEMETRY = + GraphQLTelemetry.builder(openTelemetry) + .setCaptureQuery(config.captureQuery) + .setSanitizeQuery(config.querySanitizerEnabled) + .setAddOperationNameToSpanName(config.addOperationNameToSpanName) + .build(); + } public static Instrumentation addInstrumentation(Instrumentation instrumentation) { Instrumentation ourInstrumentation = TELEMETRY.newInstrumentation(); return InstrumentationUtil.addInstrumentation(instrumentation, ourInstrumentation); } + + // instrumentation/development: + // java: + // graphql: + // capture_query: true + // query_sanitizer: + // enabled: true + // add_operation_name_to_span_name: + // enabled: false + private static final class Configuration { + + private final boolean captureQuery; + private final boolean querySanitizerEnabled; + private final boolean addOperationNameToSpanName; + + Configuration(OpenTelemetry openTelemetry) { + DeclarativeConfigProperties javaConfig = empty(); + if (openTelemetry instanceof ExtendedOpenTelemetry) { + ExtendedOpenTelemetry extendedOpenTelemetry = (ExtendedOpenTelemetry) openTelemetry; + DeclarativeConfigProperties instrumentationConfig = + extendedOpenTelemetry.getConfigProvider().getInstrumentationConfig(); + if (instrumentationConfig != null) { + javaConfig = instrumentationConfig.getStructured("java", empty()); + } + } + DeclarativeConfigProperties graphqlConfig = javaConfig.getStructured("graphql", empty()); + + this.captureQuery = graphqlConfig.getBoolean("capture_query", true); + this.querySanitizerEnabled = + graphqlConfig.getStructured("query_sanitizer", empty()).getBoolean("enabled", true); + this.addOperationNameToSpanName = + graphqlConfig + .getStructured("add_operation_name_to_span_name", empty()) + .getBoolean("enabled", false); + } + } + + private GraphqlSingletons() {} } diff --git a/instrumentation/graphql-java/graphql-java-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/graphql/v20_0/GraphqlSingletons.java b/instrumentation/graphql-java/graphql-java-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/graphql/v20_0/GraphqlSingletons.java index 99845529f985..84a9dbafb601 100644 --- a/instrumentation/graphql-java/graphql-java-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/graphql/v20_0/GraphqlSingletons.java +++ b/instrumentation/graphql-java/graphql-java-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/graphql/v20_0/GraphqlSingletons.java @@ -5,44 +5,83 @@ package io.opentelemetry.javaagent.instrumentation.graphql.v20_0; +import static io.opentelemetry.api.incubator.config.DeclarativeConfigProperties.empty; + import graphql.execution.instrumentation.Instrumentation; import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.incubator.ExtendedOpenTelemetry; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; import io.opentelemetry.instrumentation.graphql.internal.InstrumentationUtil; import io.opentelemetry.instrumentation.graphql.v20_0.GraphQLTelemetry; -import io.opentelemetry.javaagent.bootstrap.internal.AgentInstrumentationConfig; public final class GraphqlSingletons { - private static final boolean CAPTURE_QUERY = - AgentInstrumentationConfig.get() - .getBoolean("otel.instrumentation.graphql.capture-query", true); - private static final boolean QUERY_SANITIZATION_ENABLED = - AgentInstrumentationConfig.get() - .getBoolean("otel.instrumentation.graphql.query-sanitizer.enabled", true); - private static final boolean DATA_FETCHER_ENABLED = - AgentInstrumentationConfig.get() - .getBoolean("otel.instrumentation.graphql.data-fetcher.enabled", false); - private static final boolean TRIVIAL_DATA_FETCHER_ENABLED = - AgentInstrumentationConfig.get() - .getBoolean("otel.instrumentation.graphql.trivial-data-fetcher.enabled", false); - private static final boolean ADD_OPERATION_NAME_TO_SPAN_NAME = - AgentInstrumentationConfig.get() - .getBoolean( - "otel.instrumentation.graphql.add-operation-name-to-span-name.enabled", false); - - private static final GraphQLTelemetry TELEMETRY = - GraphQLTelemetry.builder(GlobalOpenTelemetry.get()) - .setCaptureQuery(CAPTURE_QUERY) - .setSanitizeQuery(QUERY_SANITIZATION_ENABLED) - .setDataFetcherInstrumentationEnabled(DATA_FETCHER_ENABLED) - .setTrivialDataFetcherInstrumentationEnabled(TRIVIAL_DATA_FETCHER_ENABLED) - .setAddOperationNameToSpanName(ADD_OPERATION_NAME_TO_SPAN_NAME) - .build(); + private static final GraphQLTelemetry TELEMETRY; - private GraphqlSingletons() {} + static { + Configuration config = new Configuration(GlobalOpenTelemetry.get()); + + TELEMETRY = + GraphQLTelemetry.builder(GlobalOpenTelemetry.get()) + .setCaptureQuery(config.captureQuery) + .setSanitizeQuery(config.sanitizeQuery) + .setDataFetcherInstrumentationEnabled(config.dataFetcherEnabled) + .setTrivialDataFetcherInstrumentationEnabled(config.trivialDataFetcherEnabled) + .setAddOperationNameToSpanName(config.addOperationNameToSpanName) + .build(); + } public static Instrumentation addInstrumentation(Instrumentation instrumentation) { Instrumentation ourInstrumentation = TELEMETRY.newInstrumentation(); return InstrumentationUtil.addInstrumentation(instrumentation, ourInstrumentation); } + + // instrumentation/development: + // java: + // graphql: + // capture_query: true + // query_sanitizer: + // enabled: true + // data_fetcher: + // enabled: false + // trivial_data_fetcher: + // enabled: false + // add_operation_name_to_span_name: + // enabled: false + private static final class Configuration { + + private final boolean captureQuery; + private final boolean sanitizeQuery; + private final boolean dataFetcherEnabled; + private final boolean trivialDataFetcherEnabled; + private final boolean addOperationNameToSpanName; + + Configuration(OpenTelemetry openTelemetry) { + DeclarativeConfigProperties javaConfig = empty(); + if (openTelemetry instanceof ExtendedOpenTelemetry) { + ExtendedOpenTelemetry extendedOpenTelemetry = (ExtendedOpenTelemetry) openTelemetry; + DeclarativeConfigProperties instrumentationConfig = + extendedOpenTelemetry.getConfigProvider().getInstrumentationConfig(); + if (instrumentationConfig != null) { + javaConfig = instrumentationConfig.getStructured("java", empty()); + } + } + DeclarativeConfigProperties graphqlConfig = javaConfig.getStructured("graphql", empty()); + + this.captureQuery = graphqlConfig.getBoolean("capture_query", true); + this.sanitizeQuery = + graphqlConfig.getStructured("query_sanitizer", empty()).getBoolean("enabled", true); + this.dataFetcherEnabled = + graphqlConfig.getStructured("data_fetcher", empty()).getBoolean("enabled", false); + this.trivialDataFetcherEnabled = + graphqlConfig.getStructured("trivial_data_fetcher", empty()).getBoolean("enabled", false); + this.addOperationNameToSpanName = + graphqlConfig + .getStructured("add_operation_name_to_span_name", empty()) + .getBoolean("enabled", false); + } + } + + private GraphqlSingletons() {} } diff --git a/instrumentation/methods/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/methods/MethodConfiguration.java b/instrumentation/methods/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/methods/MethodConfiguration.java new file mode 100644 index 000000000000..4565ca08bbe0 --- /dev/null +++ b/instrumentation/methods/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/methods/MethodConfiguration.java @@ -0,0 +1,125 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.methods; + +import static io.opentelemetry.api.incubator.config.DeclarativeConfigProperties.empty; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonMap; + +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.incubator.ExtendedOpenTelemetry; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.tooling.config.MethodsConfigurationParser; +import java.util.ArrayList; +import java.util.Collection; +import java.util.EnumMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class MethodConfiguration { + + private static final Logger logger = Logger.getLogger(MethodConfiguration.class.getName()); + + private final List typeInstrumentations; + + MethodConfiguration(OpenTelemetry openTelemetry) { + DeclarativeConfigProperties javaConfig = empty(); + if (openTelemetry instanceof ExtendedOpenTelemetry) { + ExtendedOpenTelemetry extendedOpenTelemetry = (ExtendedOpenTelemetry) openTelemetry; + DeclarativeConfigProperties instrumentationConfig = + extendedOpenTelemetry.getConfigProvider().getInstrumentationConfig(); + if (instrumentationConfig != null) { + javaConfig = instrumentationConfig.getStructured("java", empty()); + } + } + DeclarativeConfigProperties methodsConfig = javaConfig.getStructured("methods", empty()); + + this.typeInstrumentations = parse(methodsConfig); + } + + List typeInstrumentations() { + return typeInstrumentations; + } + + private static List parse(DeclarativeConfigProperties methods) { + // First try structured declarative config (YAML format) + List includeList = methods.getStructuredList("include"); + if (includeList != null) { + return includeList.stream() + .flatMap(MethodConfiguration::parseMethodInstrumentation) + .collect(Collectors.toList()); + } + + // Fall back to old string property format + String include = methods.getString("include"); + if (include != null) { + return parseConfigString(include); + } + + return emptyList(); + } + + private static Stream parseMethodInstrumentation( + DeclarativeConfigProperties config) { + String clazz = config.getString("class"); + if (isNullOrEmpty(clazz)) { + logger.log(Level.WARNING, "Invalid methods configuration - class name missing: {0}", config); + return Stream.empty(); + } + + Map> methodNames = new EnumMap<>(SpanKind.class); + for (DeclarativeConfigProperties method : config.getStructuredList("methods", emptyList())) { + String methodName = method.getString("name"); + if (isNullOrEmpty(methodName)) { + logger.log( + Level.WARNING, "Invalid methods configuration - method name missing: {0}", method); + continue; + } + String spanKind = method.getString("span_kind", "INTERNAL"); + try { + methodNames + .computeIfAbsent( + SpanKind.valueOf(spanKind.toUpperCase(Locale.ROOT)), unused -> new ArrayList<>()) + .add(methodName); + } catch (IllegalArgumentException e) { + logger.log( + Level.WARNING, + "Invalid methods configuration - unknown span_kind: {0} for method: {1}", + new Object[] {spanKind, methodName}); + } + } + + if (methodNames.isEmpty()) { + logger.log(Level.WARNING, "Invalid methods configuration - no methods defined: {0}", config); + return Stream.empty(); + } + + return Stream.of(new MethodInstrumentation(clazz, methodNames)); + } + + private static boolean isNullOrEmpty(String s) { + return s == null || s.isEmpty(); + } + + private static List parseConfigString(String include) { + Map> classMethodsToTrace = MethodsConfigurationParser.parse(include); + return classMethodsToTrace.entrySet().stream() + .filter(e -> !e.getValue().isEmpty()) + .map( + e -> + new MethodInstrumentation( + e.getKey(), singletonMap(SpanKind.INTERNAL, e.getValue()))) + .collect(Collectors.toList()); + } +} diff --git a/instrumentation/methods/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/methods/MethodInstrumentationModule.java b/instrumentation/methods/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/methods/MethodInstrumentationModule.java index 71fe619da4db..0d3f7cb79429 100644 --- a/instrumentation/methods/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/methods/MethodInstrumentationModule.java +++ b/instrumentation/methods/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/methods/MethodInstrumentationModule.java @@ -10,24 +10,17 @@ import static java.util.Collections.singletonMap; import com.google.auto.service.AutoService; +import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.trace.SpanKind; -import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig; -import io.opentelemetry.javaagent.bootstrap.internal.AgentInstrumentationConfig; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule; -import io.opentelemetry.javaagent.tooling.config.MethodsConfigurationParser; import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; @AutoService(InstrumentationModule.class) public class MethodInstrumentationModule extends InstrumentationModule implements ExperimentalInstrumentationModule { - private static final String TRACE_METHODS_CONFIG = "otel.instrumentation.methods.include"; - private final List typeInstrumentations; public MethodInstrumentationModule() { @@ -36,11 +29,8 @@ public MethodInstrumentationModule() { } private static List createInstrumentations() { - InstrumentationConfig config = AgentInstrumentationConfig.get(); - List list = - config.isDeclarative() - ? MethodsConfig.parseDeclarativeConfig(config.getDeclarativeConfig("methods")) - : parseConfigProperties(); + MethodConfiguration config = new MethodConfiguration(GlobalOpenTelemetry.get()); + List list = config.typeInstrumentations(); // ensure that there is at least one instrumentation so that muzzle reference collection could // work if (list.isEmpty()) { @@ -50,20 +40,6 @@ private static List createInstrumentations() { return list; } - private static List parseConfigProperties() { - Map> classMethodsToTrace = - MethodsConfigurationParser.parse( - AgentInstrumentationConfig.get().getString(TRACE_METHODS_CONFIG)); - - return classMethodsToTrace.entrySet().stream() - .filter(e -> !e.getValue().isEmpty()) - .map( - e -> - new MethodInstrumentation( - e.getKey(), singletonMap(SpanKind.INTERNAL, e.getValue()))) - .collect(Collectors.toList()); - } - @Override public List typeInstrumentations() { return typeInstrumentations; diff --git a/instrumentation/methods/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/methods/MethodsConfig.java b/instrumentation/methods/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/methods/MethodsConfig.java deleted file mode 100644 index 692ede856414..000000000000 --- a/instrumentation/methods/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/methods/MethodsConfig.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.instrumentation.methods; - -import static java.util.Collections.emptyList; - -import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; -import io.opentelemetry.api.trace.SpanKind; -import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; -import java.util.ArrayList; -import java.util.Collection; -import java.util.EnumMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class MethodsConfig { - - private static final Logger logger = Logger.getLogger(MethodsConfig.class.getName()); - - private MethodsConfig() {} - - static List parseDeclarativeConfig(DeclarativeConfigProperties methods) { - return methods.getStructuredList("include", emptyList()).stream() - .flatMap(MethodsConfig::parseMethodInstrumentation) - .collect(Collectors.toList()); - } - - private static Stream parseMethodInstrumentation( - DeclarativeConfigProperties config) { - String clazz = config.getString("class"); - if (isNullOrEmpty(clazz)) { - logger.log(Level.WARNING, "Invalid methods configuration - class name missing: {0}", config); - return Stream.empty(); - } - - Map> methodNames = new EnumMap<>(SpanKind.class); - for (DeclarativeConfigProperties method : config.getStructuredList("methods", emptyList())) { - String methodName = method.getString("name"); - if (isNullOrEmpty(methodName)) { - logger.log( - Level.WARNING, "Invalid methods configuration - method name missing: {0}", method); - continue; - } - String spanKind = method.getString("span_kind", "INTERNAL"); - try { - methodNames - .computeIfAbsent( - SpanKind.valueOf(spanKind.toUpperCase(Locale.ROOT)), unused -> new ArrayList<>()) - .add(methodName); - } catch (IllegalArgumentException e) { - logger.log( - Level.WARNING, - "Invalid methods configuration - unknown span_kind: {0} for method: {1}", - new Object[] {spanKind, methodName}); - } - } - - if (methodNames.isEmpty()) { - logger.log(Level.WARNING, "Invalid methods configuration - no methods defined: {0}", config); - return Stream.empty(); - } - - return Stream.of(new MethodInstrumentation(clazz, methodNames)); - } - - private static boolean isNullOrEmpty(String s) { - return s == null || s.isEmpty(); - } -} diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ExtendedOpenTelemetrySdkWrapper.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ExtendedOpenTelemetrySdkWrapper.java new file mode 100644 index 000000000000..eef2c357b590 --- /dev/null +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ExtendedOpenTelemetrySdkWrapper.java @@ -0,0 +1,30 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.tooling; + +import io.opentelemetry.api.incubator.ExtendedOpenTelemetry; +import io.opentelemetry.api.incubator.config.ConfigProvider; +import io.opentelemetry.sdk.OpenTelemetrySdk; + +final class ExtendedOpenTelemetrySdkWrapper extends OpenTelemetrySdk + implements ExtendedOpenTelemetry { + + private final ConfigProvider configProvider; + + ExtendedOpenTelemetrySdkWrapper(OpenTelemetrySdk delegate, ConfigProvider configProvider) { + super( + delegate.getSdkTracerProvider(), + delegate.getSdkMeterProvider(), + delegate.getSdkLoggerProvider(), + delegate.getPropagators()); + this.configProvider = configProvider; + } + + @Override + public ConfigProvider getConfigProvider() { + return configProvider; + } +} diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/OpenTelemetryInstaller.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/OpenTelemetryInstaller.java index ff6cb22411d1..9e9789145f14 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/OpenTelemetryInstaller.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/OpenTelemetryInstaller.java @@ -7,15 +7,18 @@ import static io.opentelemetry.api.incubator.config.DeclarativeConfigProperties.empty; +import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.incubator.ExtendedOpenTelemetry; import io.opentelemetry.api.incubator.config.ConfigProvider; import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.instrumentation.config.bridge.ConfigPropertiesBackedConfigProvider; import io.opentelemetry.instrumentation.config.bridge.DeclarativeConfigPropertiesBridgeBuilder; import io.opentelemetry.javaagent.bootstrap.OpenTelemetrySdkAccess; import io.opentelemetry.javaagent.tooling.config.EarlyInitAgentConfig; import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; import io.opentelemetry.sdk.autoconfigure.SdkAutoconfigureAccess; +import io.opentelemetry.sdk.autoconfigure.internal.AutoConfigureUtil; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; import io.opentelemetry.sdk.common.CompletableResultCode; @@ -34,26 +37,32 @@ public static AutoConfiguredOpenTelemetrySdk installOpenTelemetrySdk( AutoConfiguredOpenTelemetrySdk autoConfiguredSdk = AutoConfiguredOpenTelemetrySdk.builder() - .setResultAsGlobal() + // Don't use setResultAsGlobal() - we need to wrap the SDK before setting as global .setServiceClassLoader(extensionClassLoader) .build(); OpenTelemetrySdk sdk = autoConfiguredSdk.getOpenTelemetrySdk(); + ConfigProperties configProperties = AutoConfigureUtil.getConfig(autoConfiguredSdk); + ConfigProvider configProvider; + if (configProperties != null) { + // Provide a fake declarative configuration based on config properties + // so that declarative configuration API can be used everywhere + configProvider = ConfigPropertiesBackedConfigProvider.create(configProperties); + sdk = new ExtendedOpenTelemetrySdkWrapper(sdk, configProvider); + } else { + // Provide a fake ConfigProperties until we have migrated all runtime configuration + // access to use declarative configuration API + configProvider = ((ExtendedOpenTelemetry) sdk).getConfigProvider(); + configProperties = getDeclarativeConfigBridgedProperties(earlyConfig, configProvider); + } setForceFlush(sdk); + GlobalOpenTelemetry.set(sdk); - if (sdk instanceof ExtendedOpenTelemetry) { - ConfigProvider configProvider = ((ExtendedOpenTelemetry) sdk).getConfigProvider(); - // We create a new instance of AutoConfiguredOpenTelemetrySdk, which has a ConfigProperties - // instance that can be used to read properties from the configuration file. - // This allows most instrumentations to be unaware of which configuration style is used. - return SdkAutoconfigureAccess.create( - sdk, - SdkAutoconfigureAccess.getResource(autoConfiguredSdk), - getDeclarativeConfigBridgedProperties(earlyConfig, configProvider), - configProvider); - } - - return autoConfiguredSdk; + return SdkAutoconfigureAccess.create( + sdk, + SdkAutoconfigureAccess.getResource(autoConfiguredSdk), + configProperties, + configProvider); } // Visible for testing