Rewrite global ignores matcher to IgnoredTypesConfigurer (#3298)

This commit is contained in:
Mateusz Rzeszutek 2021-06-15 18:03:28 +02:00 committed by GitHub
parent a818f14957
commit 314ba8c1e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 283 additions and 311 deletions

View File

@ -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

View File

@ -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
}

View File

@ -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;

View File

@ -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 {

View File

@ -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_");
}
}

View File

@ -5,7 +5,7 @@
package io.opentelemetry.javaagent.tooling.ignore;
enum IgnoreAllow {
public enum IgnoreAllow {
IGNORE,
ALLOW
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}

View File

@ -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");
}
}

View File

@ -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 {

View File

@ -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