/* * Copyright The OpenTelemetry Authors * SPDX-License-Identifier: Apache-2.0 */ import com.google.common.reflect.ClassPath import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification import io.opentelemetry.instrumentation.test.utils.ClasspathUtils import io.opentelemetry.javaagent.bootstrap.BootstrapPackagePrefixesHolder import org.slf4j.LoggerFactory import java.util.concurrent.TimeoutException // this test is run using // -Dotel.javaagent.exclude-classes=config.exclude.packagename.*,config.exclude.SomeClass,config.exclude.SomeClass$NestedClass // (see integration-tests.gradle) class AgentInstrumentationSpecificationTest extends AgentInstrumentationSpecification { private static final ClassLoader BOOTSTRAP_CLASSLOADER = null public static final List BOOTSTRAP_PACKAGE_PREFIXES = BootstrapPackagePrefixesHolder.getBoostrapPackagePrefixes() def "classpath setup"() { setup: final List bootstrapClassesIncorrectlyLoaded = [] for (ClassPath.ClassInfo info : getTestClasspath().getAllClasses()) { for (int i = 0; i < BOOTSTRAP_PACKAGE_PREFIXES.size(); ++i) { if (info.getName().startsWith(BOOTSTRAP_PACKAGE_PREFIXES[i])) { Class bootstrapClass = Class.forName(info.getName()) def loader try { loader = bootstrapClass.getClassLoader() } catch (NoClassDefFoundError e) { // some classes in com.google.errorprone.annotations cause groovy to throw // java.lang.NoClassDefFoundError: [Ljavax/lang/model/element/Modifier; break } if (loader != BOOTSTRAP_CLASSLOADER) { bootstrapClassesIncorrectlyLoaded.add(bootstrapClass) } break } } } expect: bootstrapClassesIncorrectlyLoaded == [] } def "waiting for child spans times out"() { when: runWithSpan("parent") { waitForTraces(1) } then: thrown(TimeoutException) } def "logging works"() { when: LoggerFactory.getLogger(AgentInstrumentationSpecificationTest).debug("hello") then: noExceptionThrown() } def "excluded classes are not instrumented"() { when: runWithSpan("parent") { subject.getConstructor().newInstance().run() } then: assertTraces(1) { trace(0, spanName ? 2 : 1) { span(0) { name "parent" } if (spanName) { span(1) { name spanName childOf span(0) } } } } where: subject | spanName config.SomeClass | "SomeClass.run" config.SomeClass.NestedClass | "NestedClass.run" config.exclude.SomeClass | null config.exclude.SomeClass.NestedClass | null config.exclude.packagename.SomeClass | null config.exclude.packagename.SomeClass.NestedClass | null } def "test unblocked by completed span"() { setup: runWithSpan("parent") { runWithSpan("child") {} } expect: assertTraces(1) { trace(0, 2) { span(0) { name "parent" hasNoParent() } span(1) { name "child" childOf span(0) } } } } private static ClassPath getTestClasspath() { ClassLoader testClassLoader = ClasspathUtils.getClassLoader() if (!(testClassLoader instanceof URLClassLoader)) { // java9's system loader does not extend URLClassLoader // which breaks Guava ClassPath lookup testClassLoader = buildJavaClassPathClassLoader() } try { return ClassPath.from(testClassLoader) } catch (IOException e) { throw new IllegalStateException(e) } } /** * Parse JVM classpath and return ClassLoader containing all classpath entries. Inspired by Guava. */ private static ClassLoader buildJavaClassPathClassLoader() { List urls = new ArrayList<>() for (String entry : getClasspath()) { try { try { urls.add(new File(entry).toURI().toURL()) } catch (SecurityException e) { // File.toURI checks to see if the file is a directory urls.add(new URL("file", null, new File(entry).getAbsolutePath())) } } catch (MalformedURLException e) { throw new IllegalStateException(e) } } return new URLClassLoader(urls.toArray(new URL[0]), (ClassLoader) null) } private static String[] getClasspath() { return System.getProperty("java.class.path").split(System.getProperty("path.separator")) } }