Skip to content

Commit

Permalink
Merge branch 'otel-fork-updated' into correct-history
Browse files Browse the repository at this point in the history
  • Loading branch information
trask committed Mar 28, 2021
2 parents b034c6b + 95843e6 commit c0e4fc0
Show file tree
Hide file tree
Showing 19 changed files with 321 additions and 76 deletions.
13 changes: 13 additions & 0 deletions instrumentation/azure-sdk/javaagent/azure-sdk-javaagent.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
apply from: "$rootDir/gradle/instrumentation.gradle"

configurations {
testRuntime.exclude group: "com.azure", module: "azure-core-tracing-opentelemetry"
}

dependencies {
implementation(group: "com.azure", name: "azure-core-tracing-opentelemetry", version: "1.0.0-beta.8") {
exclude(group: "com.azure", module: "azure-core")
}

library group: "com.azure", name: "azure-core", version: "1.14.0"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.azuresdk;

import static io.opentelemetry.javaagent.tooling.bytebuddy.matcher.ClassLoaderMatcher.hasClassesNamed;
import static java.util.Collections.emptyMap;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.not;

import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.tooling.InstrumentationModule;
import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;

@AutoService(InstrumentationModule.class)
public class AzureSdkInstrumentationModule extends InstrumentationModule {
public AzureSdkInstrumentationModule() {
super("azure-sdk");
}

@Override
public boolean isHelperClass(String className) {
return className.startsWith("com.azure.core.tracing.opentelemetry.");
}

@Override
public String[] helperResourceNames() {
return new String[] {
"META-INF/services/com.azure.core.http.policy.AfterRetryPolicyProvider",
"META-INF/services/com.azure.core.util.tracing.Tracer"
};
}

@Override
public ElementMatcher.Junction<ClassLoader> classLoaderMatcher() {
return hasClassesNamed("com.azure.core.util.tracing.Tracer")
.and(not(hasClassesNamed("com.azure.core.tracing.opentelemetry.OpenTelemetryTracer")));
}

@Override
public List<TypeInstrumentation> typeInstrumentations() {
return Collections.singletonList(new EmptyTypeInstrumentation());
}

public static class EmptyTypeInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<? super TypeDescription> typeMatcher() {
// we cannot use com.azure.core.util.tracing.Tracer here because one of the classes that we
// inject implements this interface, causing the interface to be loaded while it's being
// transformed, which leads to duplicate class definition error after the interface is
// transformed and the triggering class loader tries to load it.
return named("com.azure.core.util.tracing.TracerProxy");
}

@Override
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
// Nothing to instrument, no methods to match
return emptyMap();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

import com.azure.core.util.tracing.TracerProxy
import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification

class AzureSdkTest extends AgentInstrumentationSpecification {

def "test helper classes injected"() {
expect:
TracerProxy.isTracingEnabled()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,8 @@ public CouchbaseInstrumentationModule() {
}

@Override
protected String[] additionalHelperClassNames() {
return new String[] {
"com.couchbase.client.tracing.opentelemetry.OpenTelemetryRequestSpan",
"com.couchbase.client.tracing.opentelemetry.OpenTelemetryRequestTracer"
};
public boolean isHelperClass(String className) {
return className.startsWith("com.couchbase.client.tracing.opentelemetry");
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ public MonoWebClientTrace(ClientRequest request, ExchangeFunction next) {
public void subscribe(CoreSubscriber<? super ClientResponse> subscriber) {
Context parentContext = Context.current();
if (!tracer().shouldStartSpan(parentContext)) {
next.exchange(request).subscribe(subscriber);
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.description.annotation.AnnotationSource;
import net.bytebuddy.description.method.MethodDescription;
Expand Down Expand Up @@ -258,6 +259,31 @@ private String mainInstrumentationName() {
return instrumentationNames.iterator().next();
}

/**
* This is an internal helper method for muzzle code generation: generating {@code invokedynamic}
* instructions in ASM is so painful that it's much simpler and readable to just have a plain old
* Java helper function here.
*/
@SuppressWarnings("unused")
protected final Predicate<String> additionalLibraryInstrumentationPackage() {
return this::isHelperClass;
}

/**
* Instrumentation modules can override this method to specify additional packages (or classes)
* that should be treated as "library instrumentation" packages. Classes from those packages will
* be treated by muzzle as instrumentation helper classes: they will be scanned for references and
* automatically injected into the application classloader if they're used in any type
* instrumentation. The classes for which this predicate returns {@code true} will be treated as
* helper classes, in addition to the default ones defined in {@link
* InstrumentationClassPredicate}.
*
* @param className The name of the class that may or may not be a helper class.
*/
public boolean isHelperClass(String className) {
return false;
}

/**
* The actual implementation of this method is generated automatically during compilation by the
* {@link io.opentelemetry.javaagent.tooling.muzzle.collector.MuzzleCodeGenerationPlugin}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

package io.opentelemetry.javaagent.tooling.muzzle;

import java.util.function.Predicate;

public final class InstrumentationClassPredicate {
// javaagent instrumentation packages
private static final String JAVAAGENT_INSTRUMENTATION_PACKAGE =
Expand All @@ -16,16 +18,28 @@ public final class InstrumentationClassPredicate {
private static final String LIBRARY_INSTRUMENTATION_PACKAGE = "io.opentelemetry.instrumentation.";
private static final String INSTRUMENTATION_API_PACKAGE = "io.opentelemetry.instrumentation.api.";

private final Predicate<String> additionalLibraryInstrumentationPredicate;

public InstrumentationClassPredicate(
Predicate<String> additionalLibraryInstrumentationPredicate) {
this.additionalLibraryInstrumentationPredicate = additionalLibraryInstrumentationPredicate;
}

/**
* Defines which classes are treated by muzzle as "internal", "helper" instrumentation classes.
*
* <p>This set of classes is defined by a package naming convention: all javaagent and library
* instrumentation classes are treated as "helper" classes and are subjected to the reference
* collection process. All others (including {@code instrumentation-api} and {@code javaagent-api}
* modules are not scanned for references (but references to them are collected).
*
* <p>Aside from "standard" instrumentation helper class packages, instrumentation modules can
* pass an additional predicate to include instrumentation helper classes from 3rd party packages.
*/
public static boolean isInstrumentationClass(String className) {
return isJavaagentInstrumentationClass(className) || isLibraryInstrumentationClass(className);
public boolean isInstrumentationClass(String className) {
return isJavaagentInstrumentationClass(className)
|| isLibraryInstrumentationClass(className)
|| additionalLibraryInstrumentationPredicate.test(className);
}

private static boolean isJavaagentInstrumentationClass(String className) {
Expand All @@ -37,6 +51,4 @@ private static boolean isLibraryInstrumentationClass(String className) {
return className.startsWith(LIBRARY_INSTRUMENTATION_PACKAGE)
&& !className.startsWith(INSTRUMENTATION_API_PACKAGE);
}

private InstrumentationClassPredicate() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ private static class GenerateMuzzleReferenceMatcherMethodAndField extends ClassV
private final boolean frames;

private String instrumentationClassName;
private InstrumentationModule instrumenter;
private InstrumentationModule instrumentationModule;

public GenerateMuzzleReferenceMatcherMethodAndField(ClassVisitor classVisitor, boolean frames) {
super(Opcodes.ASM7, classVisitor);
Expand All @@ -90,7 +90,7 @@ public void visit(
String[] interfaces) {
this.instrumentationClassName = name;
try {
instrumenter =
instrumentationModule =
(InstrumentationModule)
MuzzleCodeGenerator.class
.getClassLoader()
Expand Down Expand Up @@ -143,15 +143,15 @@ public void visitEnd() {

private ReferenceCollector collectReferences() {
Set<String> adviceClassNames =
instrumenter.typeInstrumentations().stream()
instrumentationModule.typeInstrumentations().stream()
.flatMap(typeInstrumentation -> typeInstrumentation.transformers().values().stream())
.collect(Collectors.toSet());

ReferenceCollector collector = new ReferenceCollector();
ReferenceCollector collector = new ReferenceCollector(instrumentationModule::isHelperClass);
for (String adviceClass : adviceClassNames) {
collector.collectReferencesFromAdvice(adviceClass);
}
for (String resource : instrumenter.helperResourceNames()) {
for (String resource : instrumentationModule.helperResourceNames()) {
collector.collectReferencesFromResource(resource);
}
return collector;
Expand Down Expand Up @@ -204,8 +204,9 @@ private void generateMuzzleReferenceMatcherMethod(ReferenceCollector collector)
* if (null == this.muzzleReferenceMatcher) {
* this.muzzleReferenceMatcher = new ReferenceMatcher(this.getAllHelperClassNames(),
* new Reference[]{
* //reference builders
* });
* // reference builders
* },
* this.additionalLibraryInstrumentationPackage());
* }
* return this.muzzleReferenceMatcher;
* }
Expand Down Expand Up @@ -467,11 +468,19 @@ private void generateMuzzleReferenceMatcherMethod(ReferenceCollector collector)
mv.visitInsn(Opcodes.AASTORE);
}

mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitMethodInsn(
Opcodes.INVOKEVIRTUAL,
instrumentationClassName,
"additionalLibraryInstrumentationPackage",
"()Ljava/util/function/Predicate;",
false);

mv.visitMethodInsn(
Opcodes.INVOKESPECIAL,
"io/opentelemetry/javaagent/tooling/muzzle/matcher/ReferenceMatcher",
"<init>",
"(Ljava/util/List;[Lio/opentelemetry/javaagent/tooling/muzzle/Reference;)V",
"(Ljava/util/List;[Lio/opentelemetry/javaagent/tooling/muzzle/Reference;Ljava/util/function/Predicate;)V",
false);
mv.visitFieldInsn(
Opcodes.PUTFIELD,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@

package io.opentelemetry.javaagent.tooling.muzzle.collector;

import static io.opentelemetry.javaagent.tooling.muzzle.InstrumentationClassPredicate.isInstrumentationClass;

import io.opentelemetry.javaagent.tooling.Utils;
import io.opentelemetry.javaagent.tooling.muzzle.InstrumentationClassPredicate;
import io.opentelemetry.javaagent.tooling.muzzle.Reference;
import io.opentelemetry.javaagent.tooling.muzzle.Reference.Flag;
import io.opentelemetry.javaagent.tooling.muzzle.Reference.Flag.ManifestationFlag;
Expand Down Expand Up @@ -108,7 +107,9 @@ private static Type underlyingType(Type type) {
return type;
}

private final InstrumentationClassPredicate instrumentationClassPredicate;
private final boolean isAdviceClass;

private final Map<String, Reference> references = new LinkedHashMap<>();
private final Set<String> helperClasses = new HashSet<>();
// helper super classes which are themselves also helpers
Expand All @@ -117,8 +118,10 @@ private static Type underlyingType(Type type) {
private String refSourceClassName;
private Type refSourceType;

ReferenceCollectingClassVisitor(boolean isAdviceClass) {
ReferenceCollectingClassVisitor(
InstrumentationClassPredicate instrumentationClassPredicate, boolean isAdviceClass) {
super(Opcodes.ASM7);
this.instrumentationClassPredicate = instrumentationClassPredicate;
this.isAdviceClass = isAdviceClass;
}

Expand All @@ -136,7 +139,7 @@ Set<String> getHelperSuperClasses() {

private void addExtendsReference(Reference ref) {
addReference(ref);
if (isInstrumentationClass(ref.getClassName())) {
if (instrumentationClassPredicate.isInstrumentationClass(ref.getClassName())) {
helperSuperClasses.add(ref.getClassName());
}
}
Expand All @@ -150,7 +153,7 @@ private void addReference(Reference ref) {
references.put(ref.getClassName(), reference.merge(ref));
}
}
if (isInstrumentationClass(ref.getClassName())) {
if (instrumentationClassPredicate.isInstrumentationClass(ref.getClassName())) {
helperClasses.add(ref.getClassName());
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
package io.opentelemetry.javaagent.tooling.muzzle.collector;

import static com.google.common.base.Preconditions.checkNotNull;
import static io.opentelemetry.javaagent.tooling.muzzle.InstrumentationClassPredicate.isInstrumentationClass;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Collections.singleton;

Expand All @@ -16,6 +15,7 @@
import com.google.common.graph.Graphs;
import com.google.common.graph.MutableGraph;
import io.opentelemetry.javaagent.tooling.Utils;
import io.opentelemetry.javaagent.tooling.muzzle.InstrumentationClassPredicate;
import io.opentelemetry.javaagent.tooling.muzzle.Reference;
import java.io.BufferedReader;
import java.io.IOException;
Expand All @@ -32,6 +32,7 @@
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import net.bytebuddy.jar.asm.ClassReader;

Expand All @@ -46,6 +47,12 @@ public class ReferenceCollector {
private final Map<String, Reference> references = new LinkedHashMap<>();
private final MutableGraph<String> helperSuperClassGraph = GraphBuilder.directed().build();
private final Set<String> visitedClasses = new HashSet<>();
private final InstrumentationClassPredicate instrumentationClassPredicate;

public ReferenceCollector(Predicate<String> libraryInstrumentationPredicate) {
this.instrumentationClassPredicate =
new InstrumentationClassPredicate(libraryInstrumentationPredicate);
}

/**
* If passed {@code resource} path points to an SPI file (either Java {@link
Expand Down Expand Up @@ -116,7 +123,8 @@ private void visitClassesAndCollectReferences(

try (InputStream in = getClassFileStream(visitedClassName)) {
// only start from method bodies for the advice class (skips class/method references)
ReferenceCollectingClassVisitor cv = new ReferenceCollectingClassVisitor(isAdviceClass);
ReferenceCollectingClassVisitor cv =
new ReferenceCollectingClassVisitor(instrumentationClassPredicate, isAdviceClass);
ClassReader reader = new ClassReader(in);
reader.accept(cv, ClassReader.SKIP_FRAMES);

Expand All @@ -125,7 +133,8 @@ private void visitClassesAndCollectReferences(
Reference reference = entry.getValue();

// Don't generate references created outside of the instrumentation package.
if (!visitedClasses.contains(refClassName) && isInstrumentationClass(refClassName)) {
if (!visitedClasses.contains(refClassName)
&& instrumentationClassPredicate.isInstrumentationClass(refClassName)) {
instrumentationQueue.add(refClassName);
}
addReference(refClassName, reference);
Expand Down
Loading

0 comments on commit c0e4fc0

Please sign in to comment.