Rewrite global ignores matcher to IgnoredTypesConfigurer (#3298)
This commit is contained in:
parent
a818f14957
commit
314ba8c1e8
|
@ -6,8 +6,10 @@
|
|||
package io.opentelemetry.benchmark;
|
||||
|
||||
import io.opentelemetry.instrumentation.api.config.Config;
|
||||
import io.opentelemetry.javaagent.tooling.AgentInstaller;
|
||||
import io.opentelemetry.javaagent.tooling.ignore.AdditionalLibraryIgnoredTypesConfigurer;
|
||||
import io.opentelemetry.javaagent.tooling.ignore.IgnoredTypesBuilderImpl;
|
||||
import io.opentelemetry.javaagent.tooling.ignore.IgnoredTypesMatcher;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
|
@ -36,7 +38,9 @@ public class IgnoredTypesMatcherBenchmark {
|
|||
static {
|
||||
IgnoredTypesBuilderImpl builder = new IgnoredTypesBuilderImpl();
|
||||
new AdditionalLibraryIgnoredTypesConfigurer().configure(Config.get(), builder);
|
||||
ignoredTypesMatcher = builder.buildIgnoredTypesMatcher();
|
||||
ignoredTypesMatcher =
|
||||
new IgnoredTypesMatcher(
|
||||
new AgentInstaller.NoopIgnoreMatcherProvider(), builder.buildIgnoredTypesTrie());
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
|
|
|
@ -45,6 +45,7 @@ dependencies {
|
|||
testImplementation "com.google.guava:guava"
|
||||
testImplementation "org.assertj:assertj-core"
|
||||
testImplementation "org.mockito:mockito-core"
|
||||
testImplementation "org.mockito:mockito-junit-jupiter"
|
||||
|
||||
instrumentationMuzzle sourceSets.main.output
|
||||
}
|
||||
|
|
|
@ -8,10 +8,7 @@ package io.opentelemetry.javaagent.tooling;
|
|||
import static io.opentelemetry.javaagent.bootstrap.AgentInitializer.isJavaBefore9;
|
||||
import static io.opentelemetry.javaagent.tooling.SafeServiceLoader.loadOrdered;
|
||||
import static io.opentelemetry.javaagent.tooling.Utils.getResourceName;
|
||||
import static io.opentelemetry.javaagent.tooling.matcher.GlobalIgnoresMatcher.globalIgnoresMatcher;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.any;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.none;
|
||||
|
||||
import io.opentelemetry.instrumentation.api.config.Config;
|
||||
import io.opentelemetry.javaagent.bootstrap.AgentClassLoader;
|
||||
|
@ -25,17 +22,16 @@ import io.opentelemetry.javaagent.spi.IgnoreMatcherProvider;
|
|||
import io.opentelemetry.javaagent.tooling.config.ConfigInitializer;
|
||||
import io.opentelemetry.javaagent.tooling.context.FieldBackedProvider;
|
||||
import io.opentelemetry.javaagent.tooling.ignore.IgnoredTypesBuilderImpl;
|
||||
import io.opentelemetry.javaagent.tooling.ignore.IgnoredTypesMatcher;
|
||||
import io.opentelemetry.javaagent.tooling.matcher.GlobalClassloaderIgnoresMatcher;
|
||||
import java.lang.instrument.Instrumentation;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
@ -44,9 +40,6 @@ import net.bytebuddy.agent.builder.ResettableClassFileTransformer;
|
|||
import net.bytebuddy.description.type.TypeDefinition;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
import net.bytebuddy.dynamic.DynamicType;
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
import net.bytebuddy.matcher.NameMatcher;
|
||||
import net.bytebuddy.matcher.StringSetMatcher;
|
||||
import net.bytebuddy.utility.JavaModule;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -56,7 +49,6 @@ public class AgentInstaller {
|
|||
private static final Logger log;
|
||||
|
||||
private static final String JAVAAGENT_ENABLED_CONFIG = "otel.javaagent.enabled";
|
||||
private static final String EXCLUDED_CLASSES_CONFIG = "otel.javaagent.exclude-classes";
|
||||
|
||||
// This property may be set to force synchronous AgentListener#afterAgent() execution: the
|
||||
// condition for delaying the AgentListener initialization is pretty broad and in case it covers
|
||||
|
@ -122,11 +114,7 @@ public class AgentInstaller {
|
|||
|
||||
FieldBackedProvider.resetContextMatchers();
|
||||
|
||||
IgnoreMatcherProvider ignoreMatcherProvider = loadIgnoreMatcherProvider();
|
||||
log.debug(
|
||||
"Ignore matcher provider {} will be used", ignoreMatcherProvider.getClass().getName());
|
||||
|
||||
AgentBuilder.Ignored ignoredAgentBuilder =
|
||||
AgentBuilder agentBuilder =
|
||||
new AgentBuilder.Default()
|
||||
.disableClassFormatChanges()
|
||||
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
|
||||
|
@ -134,19 +122,13 @@ public class AgentInstaller {
|
|||
.with(AgentBuilder.DescriptionStrategy.Default.POOL_ONLY)
|
||||
.with(AgentTooling.poolStrategy())
|
||||
.with(new ClassLoadListener())
|
||||
.with(AgentTooling.locationStrategy())
|
||||
// FIXME: we cannot enable it yet due to BB/JVM bug, see
|
||||
// https://github.com/raphw/byte-buddy/issues/558
|
||||
// .with(AgentBuilder.LambdaInstrumentationStrategy.ENABLED)
|
||||
.ignore(any(), GlobalClassloaderIgnoresMatcher.skipClassLoader(ignoreMatcherProvider));
|
||||
.with(AgentTooling.locationStrategy());
|
||||
// FIXME: we cannot enable it yet due to BB/JVM bug, see
|
||||
// https://github.com/raphw/byte-buddy/issues/558
|
||||
// .with(AgentBuilder.LambdaInstrumentationStrategy.ENABLED)
|
||||
|
||||
ignoredAgentBuilder =
|
||||
ignoredAgentBuilder.or(
|
||||
globalIgnoresMatcher(ignoreMatcherProvider, loadIgnoredTypesMatcher(config)));
|
||||
agentBuilder = configureIgnoredTypes(config, agentBuilder);
|
||||
|
||||
ignoredAgentBuilder = ignoredAgentBuilder.or(matchesConfiguredExcludes());
|
||||
|
||||
AgentBuilder agentBuilder = ignoredAgentBuilder;
|
||||
if (log.isDebugEnabled()) {
|
||||
agentBuilder =
|
||||
agentBuilder
|
||||
|
@ -180,14 +162,6 @@ public class AgentInstaller {
|
|||
return resettableClassFileTransformer;
|
||||
}
|
||||
|
||||
private static ElementMatcher<TypeDescription> loadIgnoredTypesMatcher(Config config) {
|
||||
IgnoredTypesBuilderImpl ignoredTypesBuilder = new IgnoredTypesBuilderImpl();
|
||||
for (IgnoredTypesConfigurer configurer : loadOrdered(IgnoredTypesConfigurer.class)) {
|
||||
configurer.configure(config, ignoredTypesBuilder);
|
||||
}
|
||||
return ignoredTypesBuilder.buildIgnoredTypesMatcher();
|
||||
}
|
||||
|
||||
private static void runBeforeAgentListeners(
|
||||
Iterable<AgentListener> agentListeners, Config config) {
|
||||
for (AgentListener agentListener : agentListeners) {
|
||||
|
@ -195,6 +169,23 @@ public class AgentInstaller {
|
|||
}
|
||||
}
|
||||
|
||||
private static AgentBuilder configureIgnoredTypes(Config config, AgentBuilder agentBuilder) {
|
||||
IgnoreMatcherProvider ignoreMatcherProvider = loadIgnoreMatcherProvider();
|
||||
log.debug(
|
||||
"Ignore matcher provider {} will be used", ignoreMatcherProvider.getClass().getName());
|
||||
|
||||
IgnoredTypesBuilderImpl ignoredTypesBuilder = new IgnoredTypesBuilderImpl();
|
||||
for (IgnoredTypesConfigurer configurer : loadOrdered(IgnoredTypesConfigurer.class)) {
|
||||
configurer.configure(config, ignoredTypesBuilder);
|
||||
}
|
||||
|
||||
return agentBuilder
|
||||
.ignore(any(), GlobalClassloaderIgnoresMatcher.skipClassLoader(ignoreMatcherProvider))
|
||||
.or(
|
||||
new IgnoredTypesMatcher(
|
||||
ignoreMatcherProvider, ignoredTypesBuilder.buildIgnoredTypesTrie()));
|
||||
}
|
||||
|
||||
private static void runAfterAgentListeners(
|
||||
Iterable<AgentListener> agentListeners, Config config) {
|
||||
// java.util.logging.LogManager maintains a final static LogManager, which is created during
|
||||
|
@ -256,33 +247,6 @@ public class AgentInstaller {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: rewrite to an IgnoredTypesConfigurer
|
||||
private static ElementMatcher.Junction<? super TypeDescription> matchesConfiguredExcludes() {
|
||||
List<String> excludedClasses = Config.get().getListProperty(EXCLUDED_CLASSES_CONFIG);
|
||||
ElementMatcher.Junction<? super TypeDescription> matcher = none();
|
||||
Set<String> literals = new HashSet<>();
|
||||
List<String> prefixes = new ArrayList<>();
|
||||
// first accumulate by operation because a lot of work can be aggregated
|
||||
for (String excludedClass : excludedClasses) {
|
||||
excludedClass = excludedClass.trim();
|
||||
if (excludedClass.endsWith("*")) {
|
||||
// remove the trailing *
|
||||
prefixes.add(excludedClass.substring(0, excludedClass.length() - 1));
|
||||
} else {
|
||||
literals.add(excludedClass);
|
||||
}
|
||||
}
|
||||
if (!literals.isEmpty()) {
|
||||
matcher = matcher.or(new NameMatcher<>(new StringSetMatcher(literals)));
|
||||
}
|
||||
for (String prefix : prefixes) {
|
||||
// TODO - with a prefix tree this matching logic can be handled by a
|
||||
// single longest common prefix query
|
||||
matcher = matcher.or(nameStartsWith(prefix));
|
||||
}
|
||||
return matcher;
|
||||
}
|
||||
|
||||
private static List<String> loadBootstrapPackagePrefixes() {
|
||||
List<String> bootstrapPackages = new ArrayList<>(Constants.BOOTSTRAP_PACKAGE_PREFIXES);
|
||||
Iterable<BootstrapPackagesProvider> bootstrapPackagesProviders =
|
||||
|
@ -531,7 +495,9 @@ public class AgentInstaller {
|
|||
"{} loaded on {}", AgentInstaller.class.getName(), AgentInstaller.class.getClassLoader());
|
||||
}
|
||||
|
||||
private static class NoopIgnoreMatcherProvider implements IgnoreMatcherProvider {
|
||||
/** This class will be removed together with {@link IgnoreMatcherProvider}. */
|
||||
@Deprecated
|
||||
public static final class NoopIgnoreMatcherProvider implements IgnoreMatcherProvider {
|
||||
@Override
|
||||
public Result classloader(ClassLoader classLoader) {
|
||||
return Result.DEFAULT;
|
||||
|
|
|
@ -9,15 +9,14 @@ import com.google.auto.service.AutoService;
|
|||
import io.opentelemetry.instrumentation.api.config.Config;
|
||||
import io.opentelemetry.javaagent.extension.ignore.IgnoredTypesBuilder;
|
||||
import io.opentelemetry.javaagent.extension.ignore.IgnoredTypesConfigurer;
|
||||
import io.opentelemetry.javaagent.tooling.matcher.GlobalIgnoresMatcher;
|
||||
|
||||
/**
|
||||
* Additional global ignore settings that are used to reduce number of classes we try to apply
|
||||
* expensive matchers to.
|
||||
*
|
||||
* <p>This is separated from {@link GlobalIgnoresMatcher} to allow for better testing. The idea is
|
||||
* that we should be able to remove this matcher from the agent and all tests should still pass.
|
||||
* Moreover, no classes matched by this matcher should be modified during test run.
|
||||
* <p>This is separated from {@link GlobalIgnoredTypesConfigurer} to allow for better testing. The
|
||||
* idea is that we should be able to remove this matcher from the agent and all tests should still
|
||||
* pass. Moreover, no classes matched by this matcher should be modified during test run.
|
||||
*/
|
||||
@AutoService(IgnoredTypesConfigurer.class)
|
||||
public class AdditionalLibraryIgnoredTypesConfigurer implements IgnoredTypesConfigurer {
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.tooling.ignore;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import io.opentelemetry.instrumentation.api.config.Config;
|
||||
import io.opentelemetry.javaagent.extension.ignore.IgnoredTypesBuilder;
|
||||
import io.opentelemetry.javaagent.extension.ignore.IgnoredTypesConfigurer;
|
||||
|
||||
@AutoService(IgnoredTypesConfigurer.class)
|
||||
public class GlobalIgnoredTypesConfigurer implements IgnoredTypesConfigurer {
|
||||
|
||||
@Override
|
||||
public void configure(Config config, IgnoredTypesBuilder builder) {
|
||||
builder
|
||||
.ignoreClass("org.gradle.")
|
||||
.ignoreClass("net.bytebuddy.")
|
||||
.ignoreClass("jdk.")
|
||||
.ignoreClass("org.aspectj.")
|
||||
.ignoreClass("datadog.")
|
||||
.ignoreClass("com.intellij.rt.debugger.")
|
||||
.ignoreClass("com.p6spy.")
|
||||
.ignoreClass("com.dynatrace.")
|
||||
.ignoreClass("com.jloadtrace.")
|
||||
.ignoreClass("com.appdynamics.")
|
||||
.ignoreClass("com.newrelic.agent.")
|
||||
.ignoreClass("com.newrelic.api.agent.")
|
||||
.ignoreClass("com.nr.agent.")
|
||||
.ignoreClass("com.singularity.")
|
||||
.ignoreClass("com.jinspired.")
|
||||
.ignoreClass("org.jinspired.");
|
||||
|
||||
// allow JDK HttpClient
|
||||
builder.allowClass("jdk.internal.net.http.");
|
||||
|
||||
// groovy
|
||||
builder
|
||||
.ignoreClass("org.groovy.")
|
||||
.ignoreClass("org.apache.groovy.")
|
||||
.ignoreClass("org.codehaus.groovy.")
|
||||
// We seem to instrument some classes in runtime
|
||||
.allowClass("org.codehaus.groovy.runtime.");
|
||||
|
||||
// clojure
|
||||
builder.ignoreClass("clojure.").ignoreClass("$fn__");
|
||||
|
||||
builder
|
||||
.ignoreClass("io.opentelemetry.javaagent.")
|
||||
// FIXME: We should remove this once
|
||||
// https://github.com/raphw/byte-buddy/issues/558 is fixed
|
||||
.allowClass("io.opentelemetry.javaagent.instrumentation.api.concurrent.RunnableWrapper")
|
||||
.allowClass("io.opentelemetry.javaagent.instrumentation.api.concurrent.CallableWrapper");
|
||||
|
||||
builder
|
||||
.ignoreClass("java.")
|
||||
.allowClass("java.net.URL")
|
||||
.allowClass("java.net.HttpURLConnection")
|
||||
.allowClass("java.net.URLClassLoader")
|
||||
.allowClass("java.rmi.")
|
||||
.allowClass("java.util.concurrent.")
|
||||
.allowClass("java.lang.reflect.Proxy")
|
||||
.allowClass("java.lang.ClassLoader")
|
||||
// Concurrent instrumentation modifies the structure of
|
||||
// Cleaner class incompatibly with java9+ modules.
|
||||
// Working around until a long-term fix for modules can be
|
||||
// put in place.
|
||||
.allowClass("java.util.logging.")
|
||||
.ignoreClass("java.util.logging.LogManager$Cleaner");
|
||||
|
||||
builder
|
||||
.ignoreClass("com.sun.")
|
||||
.allowClass("com.sun.messaging.")
|
||||
.allowClass("com.sun.jersey.api.client")
|
||||
.allowClass("com.sun.appserv")
|
||||
.allowClass("com.sun.faces")
|
||||
.allowClass("com.sun.xml.ws");
|
||||
|
||||
builder
|
||||
.ignoreClass("sun.")
|
||||
.allowClass("sun.net.www.protocol.")
|
||||
.allowClass("sun.rmi.server")
|
||||
.allowClass("sun.rmi.transport")
|
||||
.allowClass("sun.net.www.http.HttpClient");
|
||||
|
||||
builder.ignoreClass("org.slf4j.").allowClass("org.slf4j.MDC");
|
||||
|
||||
builder
|
||||
.ignoreClass("org.springframework.core.$Proxy")
|
||||
// Tapestry Proxy, check only specific class that we know would be instrumented since there
|
||||
// is no common prefix for its proxies other than "$". ByteBuddy fails to instrument this
|
||||
// proxy, and as there is no reason why it should be instrumented anyway, exclude it.
|
||||
.ignoreClass("$HttpServletRequest_");
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
package io.opentelemetry.javaagent.tooling.ignore;
|
||||
|
||||
enum IgnoreAllow {
|
||||
public enum IgnoreAllow {
|
||||
IGNORE,
|
||||
ALLOW
|
||||
}
|
||||
|
|
|
@ -7,8 +7,6 @@ package io.opentelemetry.javaagent.tooling.ignore;
|
|||
|
||||
import io.opentelemetry.javaagent.extension.ignore.IgnoredTypesBuilder;
|
||||
import io.opentelemetry.javaagent.tooling.ignore.trie.Trie;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
|
||||
public class IgnoredTypesBuilderImpl implements IgnoredTypesBuilder {
|
||||
private final Trie.Builder<IgnoreAllow> ignoreMatcherTrie = Trie.newBuilder();
|
||||
|
@ -49,7 +47,7 @@ public class IgnoredTypesBuilderImpl implements IgnoredTypesBuilder {
|
|||
throw new UnsupportedOperationException("not implemented yet");
|
||||
}
|
||||
|
||||
public ElementMatcher<TypeDescription> buildIgnoredTypesMatcher() {
|
||||
return new IgnoredTypesMatcher(ignoreMatcherTrie.build());
|
||||
public Trie<IgnoreAllow> buildIgnoredTypesTrie() {
|
||||
return ignoreMatcherTrie.build();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,21 +5,66 @@
|
|||
|
||||
package io.opentelemetry.javaagent.tooling.ignore;
|
||||
|
||||
import io.opentelemetry.javaagent.spi.IgnoreMatcherProvider;
|
||||
import io.opentelemetry.javaagent.tooling.ignore.trie.Trie;
|
||||
import java.util.regex.Pattern;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
|
||||
class IgnoredTypesMatcher extends ElementMatcher.Junction.AbstractBase<TypeDescription> {
|
||||
public class IgnoredTypesMatcher extends ElementMatcher.Junction.AbstractBase<TypeDescription> {
|
||||
|
||||
private static final Pattern COM_MCHANGE_PROXY =
|
||||
Pattern.compile("com\\.mchange\\.v2\\.c3p0\\..*Proxy");
|
||||
|
||||
private final IgnoreMatcherProvider ignoreMatcherProvider;
|
||||
private final Trie<IgnoreAllow> ignoredTypes;
|
||||
|
||||
IgnoredTypesMatcher(Trie<IgnoreAllow> ignoredTypes) {
|
||||
public IgnoredTypesMatcher(
|
||||
IgnoreMatcherProvider ignoreMatcherProvider, Trie<IgnoreAllow> ignoredTypes) {
|
||||
this.ignoreMatcherProvider = ignoreMatcherProvider;
|
||||
this.ignoredTypes = ignoredTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(TypeDescription target) {
|
||||
IgnoreAllow ignored = ignoredTypes.getOrNull(target.getActualName());
|
||||
// ALLOW or null (default) mean that the type should not be ignored
|
||||
return ignored == IgnoreAllow.IGNORE;
|
||||
String name = target.getActualName();
|
||||
|
||||
// TODO: will be removed together with IgnoreMatcherProvider
|
||||
IgnoreMatcherProvider.Result ignoreResult = ignoreMatcherProvider.type(target);
|
||||
if (ignoreResult == IgnoreMatcherProvider.Result.ALLOW) {
|
||||
return false;
|
||||
} else if (ignoreResult == IgnoreMatcherProvider.Result.IGNORE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
IgnoreAllow ignored = ignoredTypes.getOrNull(name);
|
||||
if (ignored == IgnoreAllow.ALLOW) {
|
||||
return false;
|
||||
} else if (ignored == IgnoreAllow.IGNORE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// bytecode proxies typically have $$ in their name
|
||||
if (name.contains("$$")) {
|
||||
// allow scala anonymous classes
|
||||
return !name.contains("$$anon$");
|
||||
}
|
||||
|
||||
if (name.contains("$JaxbAccessor")
|
||||
|| name.contains("CGLIB$$")
|
||||
|| name.contains("javassist")
|
||||
|| name.contains(".asm.")
|
||||
|| name.contains("$__sisu")
|
||||
|| name.contains("$$EnhancerByProxool$$")
|
||||
// glassfish ejb proxy
|
||||
// We skip instrumenting these because some instrumentations e.g. jax-rs instrument methods
|
||||
// that are annotated with @Path in an interface implemented by the class. We don't really
|
||||
// want to instrument these methods in generated classes as this would create spans that
|
||||
// have the generated class name in them instead of the actual class that handles the call.
|
||||
|| name.contains("__EJB31_Generated__")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return COM_MCHANGE_PROXY.matcher(name).matches();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.tooling.ignore;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import io.opentelemetry.instrumentation.api.config.Config;
|
||||
import io.opentelemetry.javaagent.extension.ignore.IgnoredTypesBuilder;
|
||||
import io.opentelemetry.javaagent.extension.ignore.IgnoredTypesConfigurer;
|
||||
import java.util.List;
|
||||
|
||||
@AutoService(IgnoredTypesConfigurer.class)
|
||||
public class UserExcludedClassesConfigurer implements IgnoredTypesConfigurer {
|
||||
|
||||
// visible for tests
|
||||
static final String EXCLUDED_CLASSES_CONFIG = "otel.javaagent.exclude-classes";
|
||||
|
||||
@Override
|
||||
public void configure(Config config, IgnoredTypesBuilder builder) {
|
||||
List<String> excludedClasses = config.getListProperty(EXCLUDED_CLASSES_CONFIG);
|
||||
for (String excludedClass : excludedClasses) {
|
||||
excludedClass = excludedClass.trim();
|
||||
// remove the trailing *
|
||||
if (excludedClass.endsWith("*")) {
|
||||
excludedClass = excludedClass.substring(0, excludedClass.length() - 1);
|
||||
}
|
||||
builder.ignoreClass(excludedClass);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,223 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.tooling.matcher;
|
||||
|
||||
import io.opentelemetry.javaagent.spi.IgnoreMatcherProvider;
|
||||
import java.util.regex.Pattern;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
|
||||
/**
|
||||
* Global ignores matcher used by the agent.
|
||||
*
|
||||
* <p>This matcher services two main purposes:
|
||||
* <li>
|
||||
*
|
||||
* <ul>
|
||||
* Ignore classes that are unsafe or pointless to transform. 'System' level classes like jvm
|
||||
* classes or groovy classes, other tracers, debuggers, etc.
|
||||
* </ul>
|
||||
*/
|
||||
// TODO: rewrite to an IgnoredTypesConfigurer
|
||||
public class GlobalIgnoresMatcher extends ElementMatcher.Junction.AbstractBase<TypeDescription> {
|
||||
|
||||
private static final Pattern COM_MCHANGE_PROXY =
|
||||
Pattern.compile("com\\.mchange\\.v2\\.c3p0\\..*Proxy");
|
||||
|
||||
public static ElementMatcher.Junction<TypeDescription> globalIgnoresMatcher(
|
||||
IgnoreMatcherProvider ignoreMatcherProviders,
|
||||
ElementMatcher<TypeDescription> ignoredTypeMatcher) {
|
||||
return new GlobalIgnoresMatcher(ignoreMatcherProviders, ignoredTypeMatcher);
|
||||
}
|
||||
|
||||
private final IgnoreMatcherProvider ignoreMatcherProvider;
|
||||
private final ElementMatcher<TypeDescription> ignoredTypeMatcher;
|
||||
|
||||
private GlobalIgnoresMatcher(
|
||||
IgnoreMatcherProvider ignoreMatcherProvider,
|
||||
ElementMatcher<TypeDescription> ignoredTypeMatcher) {
|
||||
this.ignoreMatcherProvider = ignoreMatcherProvider;
|
||||
this.ignoredTypeMatcher = ignoredTypeMatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Be very careful about the types of matchers used in this section as they are called on every
|
||||
* class load, so they must be fast. Generally speaking try to only use name matchers as they
|
||||
* don't have to load additional info.
|
||||
*/
|
||||
@Override
|
||||
public boolean matches(TypeDescription target) {
|
||||
IgnoreMatcherProvider.Result ignoreResult = ignoreMatcherProvider.type(target);
|
||||
switch (ignoreResult) {
|
||||
case IGNORE:
|
||||
return true;
|
||||
case ALLOW:
|
||||
return false;
|
||||
case DEFAULT:
|
||||
}
|
||||
|
||||
String name = target.getActualName();
|
||||
|
||||
if (name.startsWith("jdk.internal.net.http.")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (name.startsWith("org.gradle.")
|
||||
|| name.startsWith("net.bytebuddy.")
|
||||
|| name.startsWith("jdk.")
|
||||
|| name.startsWith("org.aspectj.")
|
||||
|| name.startsWith("datadog.")
|
||||
|| name.startsWith("com.intellij.rt.debugger.")
|
||||
|| name.startsWith("com.p6spy.")
|
||||
|| name.startsWith("com.dynatrace.")
|
||||
|| name.startsWith("com.jloadtrace.")
|
||||
|| name.startsWith("com.appdynamics.")
|
||||
|| name.startsWith("com.newrelic.agent.")
|
||||
|| name.startsWith("com.newrelic.api.agent.")
|
||||
|| name.startsWith("com.nr.agent.")
|
||||
|| name.startsWith("com.singularity.")
|
||||
|| name.startsWith("com.jinspired.")
|
||||
|| name.startsWith("org.jinspired.")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// groovy
|
||||
if (name.startsWith("org.groovy.") || name.startsWith("org.apache.groovy.")) {
|
||||
return true;
|
||||
}
|
||||
if (name.startsWith("org.codehaus.groovy.")) {
|
||||
// We seem to instrument some classes in runtime
|
||||
if (name.startsWith("org.codehaus.groovy.runtime.")) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// clojure
|
||||
if (name.startsWith("clojure.") || name.contains("$fn__")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (name.startsWith("io.opentelemetry.javaagent.")) {
|
||||
// FIXME: We should remove this once
|
||||
// https://github.com/raphw/byte-buddy/issues/558 is fixed
|
||||
if (name.equals("io.opentelemetry.javaagent.instrumentation.api.concurrent.RunnableWrapper")
|
||||
|| name.equals(
|
||||
"io.opentelemetry.javaagent.instrumentation.api.concurrent.CallableWrapper")) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (name.startsWith("java.")) {
|
||||
if (name.equals("java.net.URL")
|
||||
|| name.equals("java.net.HttpURLConnection")
|
||||
|| name.equals("java.net.URLClassLoader")) {
|
||||
return false;
|
||||
}
|
||||
if (name.startsWith("java.rmi.") || name.startsWith("java.util.concurrent.")) {
|
||||
return false;
|
||||
}
|
||||
if (name.equals("java.lang.reflect.Proxy")) {
|
||||
return false;
|
||||
}
|
||||
if (name.equals("java.lang.ClassLoader")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Concurrent instrumentation modifies the structure of
|
||||
// Cleaner class incompatibly with java9+ modules.
|
||||
// Working around until a long-term fix for modules can be
|
||||
// put in place.
|
||||
if (name.startsWith("java.util.logging.")
|
||||
&& !name.equals("java.util.logging.LogManager$Cleaner")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (name.startsWith("com.sun.")) {
|
||||
if (name.startsWith("com.sun.messaging.")
|
||||
|| name.startsWith("com.sun.jersey.api.client")
|
||||
|| name.startsWith("com.sun.appserv")
|
||||
|| name.startsWith("com.sun.faces")
|
||||
|| name.startsWith("com.sun.xml.ws")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (name.startsWith("sun.")) {
|
||||
if (name.startsWith("sun.net.www.protocol.")
|
||||
|| name.startsWith("sun.rmi.server")
|
||||
|| name.startsWith("sun.rmi.transport")
|
||||
|| name.equals("sun.net.www.http.HttpClient")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (name.startsWith("org.slf4j.")) {
|
||||
if (name.equals("org.slf4j.MDC")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// bytecode proxies typically have $$ in their name
|
||||
if (name.contains("$$")) {
|
||||
// scala anonymous classes
|
||||
if (name.contains("$$anon$")) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (name.contains("$JaxbAccessor")
|
||||
|| name.contains("CGLIB$$")
|
||||
|| name.contains("javassist")
|
||||
|| name.contains(".asm.")
|
||||
|| name.contains("$__sisu")
|
||||
|| name.contains("$$EnhancerByProxool$$")
|
||||
// glassfish ejb proxy
|
||||
// We skip instrumenting these because some instrumentations e.g. jax-rs instrument methods
|
||||
// that are annotated with @Path in an interface implemented by the class. We don't really
|
||||
// want to instrument these methods in generated classes as this would create spans that
|
||||
// have the generated class name in them instead of the actual class that handles the call.
|
||||
|| name.contains("__EJB31_Generated__")
|
||||
|| name.startsWith("org.springframework.core.$Proxy")
|
||||
// Tapestry Proxy, check only specific class that we know would be instrumented since there
|
||||
// is no common prefix for its proxies other than "$". ByteBuddy fails to instrument this
|
||||
// proxy, and as there is no reason why it should be instrumented anyway, exclude it.
|
||||
|| name.startsWith("$HttpServletRequest_")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (COM_MCHANGE_PROXY.matcher(name).matches()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return ignoredTypeMatcher.matches(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "globalIgnoresMatcher()";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof GlobalIgnoresMatcher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 17;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.tooling.ignore;
|
||||
|
||||
import static io.opentelemetry.javaagent.tooling.ignore.UserExcludedClassesConfigurer.EXCLUDED_CLASSES_CONFIG;
|
||||
import static java.util.Collections.emptyMap;
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
|
||||
import io.opentelemetry.instrumentation.api.config.Config;
|
||||
import io.opentelemetry.instrumentation.api.config.ConfigBuilder;
|
||||
import io.opentelemetry.javaagent.extension.ignore.IgnoredTypesBuilder;
|
||||
import io.opentelemetry.javaagent.extension.ignore.IgnoredTypesConfigurer;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class UserExcludedClassesConfigurerTest {
|
||||
@Mock IgnoredTypesBuilder builder;
|
||||
|
||||
IgnoredTypesConfigurer underTest = new UserExcludedClassesConfigurer();
|
||||
|
||||
@Test
|
||||
void shouldAddNothingToBuilderWhenPropertyIsEmpty() {
|
||||
// when
|
||||
underTest.configure(Config.create(emptyMap()), builder);
|
||||
|
||||
// then
|
||||
verifyNoInteractions(builder);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldIgnoreClassesAndPackages() {
|
||||
// given
|
||||
Config config =
|
||||
new ConfigBuilder()
|
||||
.readProperties(
|
||||
singletonMap(
|
||||
EXCLUDED_CLASSES_CONFIG,
|
||||
"com.example.IgnoredClass,com.example.ignored.*,com.another_ignore"))
|
||||
.build();
|
||||
|
||||
// when
|
||||
underTest.configure(config, builder);
|
||||
|
||||
// then
|
||||
verify(builder).ignoreClass("com.example.IgnoredClass");
|
||||
verify(builder).ignoreClass("com.example.ignored.");
|
||||
verify(builder).ignoreClass("com.another_ignore");
|
||||
}
|
||||
}
|
|
@ -3,12 +3,11 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.tooling.ignore;
|
||||
package io.opentelemetry.javaagent.tooling.ignore.trie;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
import io.opentelemetry.javaagent.tooling.ignore.trie.Trie;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class TrieTest {
|
|
@ -7,7 +7,9 @@ package io.opentelemetry.javaagent.testing.bytebuddy;
|
|||
|
||||
import io.opentelemetry.instrumentation.api.config.Config;
|
||||
import io.opentelemetry.javaagent.tooling.ignore.AdditionalLibraryIgnoredTypesConfigurer;
|
||||
import io.opentelemetry.javaagent.tooling.ignore.IgnoreAllow;
|
||||
import io.opentelemetry.javaagent.tooling.ignore.IgnoredTypesBuilderImpl;
|
||||
import io.opentelemetry.javaagent.tooling.ignore.trie.Trie;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
@ -19,7 +21,6 @@ import java.util.function.Function;
|
|||
import net.bytebuddy.agent.builder.AgentBuilder;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
import net.bytebuddy.dynamic.DynamicType;
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
import net.bytebuddy.utility.JavaModule;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -28,17 +29,16 @@ public class TestAgentListener implements AgentBuilder.Listener {
|
|||
|
||||
private static final Logger logger = LoggerFactory.getLogger(TestAgentListener.class);
|
||||
|
||||
private static final ElementMatcher<TypeDescription> GLOBAL_LIBRARIES_IGNORES_MATCHER;
|
||||
private static final Trie<IgnoreAllow> ADDITIONAL_LIBRARIES_TRIE;
|
||||
|
||||
static {
|
||||
IgnoredTypesBuilderImpl builder = new IgnoredTypesBuilderImpl();
|
||||
new AdditionalLibraryIgnoredTypesConfigurer().configure(Config.get(), builder);
|
||||
GLOBAL_LIBRARIES_IGNORES_MATCHER = builder.buildIgnoredTypesMatcher();
|
||||
ADDITIONAL_LIBRARIES_TRIE = builder.buildIgnoredTypesTrie();
|
||||
}
|
||||
|
||||
public static void reset() {
|
||||
INSTANCE.transformedClassesNames.clear();
|
||||
INSTANCE.transformedClassesTypes.clear();
|
||||
INSTANCE.instrumentationErrorCount.set(0);
|
||||
INSTANCE.skipTransformationConditions.clear();
|
||||
INSTANCE.skipErrorConditions.clear();
|
||||
|
@ -50,9 +50,9 @@ public class TestAgentListener implements AgentBuilder.Listener {
|
|||
|
||||
public static List<String> getIgnoredButTransformedClassNames() {
|
||||
List<String> names = new ArrayList<>();
|
||||
for (TypeDescription type : INSTANCE.transformedClassesTypes) {
|
||||
if (GLOBAL_LIBRARIES_IGNORES_MATCHER.matches(type)) {
|
||||
names.add(type.getActualName());
|
||||
for (String name : INSTANCE.transformedClassesNames) {
|
||||
if (ADDITIONAL_LIBRARIES_TRIE.getOrNull(name) == IgnoreAllow.IGNORE) {
|
||||
names.add(name);
|
||||
}
|
||||
}
|
||||
return names;
|
||||
|
@ -70,8 +70,6 @@ public class TestAgentListener implements AgentBuilder.Listener {
|
|||
|
||||
private final Set<String> transformedClassesNames =
|
||||
Collections.newSetFromMap(new ConcurrentHashMap<>());
|
||||
private final Set<TypeDescription> transformedClassesTypes =
|
||||
Collections.newSetFromMap(new ConcurrentHashMap<>());
|
||||
private final AtomicInteger instrumentationErrorCount = new AtomicInteger(0);
|
||||
private final Set<Function<String, Boolean>> skipTransformationConditions =
|
||||
Collections.newSetFromMap(new ConcurrentHashMap<>());
|
||||
|
@ -97,7 +95,6 @@ public class TestAgentListener implements AgentBuilder.Listener {
|
|||
boolean loaded,
|
||||
DynamicType dynamicType) {
|
||||
transformedClassesNames.add(typeDescription.getActualName());
|
||||
transformedClassesTypes.add(typeDescription);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
Loading…
Reference in New Issue