Remove last muzzle generate method from InstrumentationModule (#4281)
* Remove last muzzle generate method from InstrumentationModule
This commit is contained in:
parent
7473eff1e6
commit
9bbd490288
|
@ -20,8 +20,10 @@ Muzzle has two phases:
|
|||
The compile-time reference collection and code generation process is implemented using a ByteBuddy
|
||||
plugin (called `MuzzleCodeGenerationPlugin`).
|
||||
|
||||
For each instrumentation module the ByteBuddy plugin collects symbols referring to both internal and
|
||||
third party APIs used by the currently processed module's type
|
||||
For each instrumentation module the ByteBuddy plugin first applies `InstrumentationModuleMuzzle`
|
||||
interface to it and then proceeds to implement all methods from that interface by generating the
|
||||
required bytecode.
|
||||
It collects symbols referring to both internal and third party APIs used by the currently processed module's type
|
||||
instrumentations (`InstrumentationModule#typeInstrumentations()`). The reference collection process
|
||||
starts from advice classes (values of the map returned by the
|
||||
`TypeInstrumentation#transformers()` method) and traverses the class graph until it encounters a
|
||||
|
@ -29,29 +31,23 @@ reference to a non-instrumentation class (determined by `InstrumentationClassPre
|
|||
the `InstrumentationModule#isHelperClass(String)` predicate). Aside from references,
|
||||
the collection process also builds a graph of dependencies between internal instrumentation helper
|
||||
classes - this dependency graph is later used to construct a list of helper classes that will be
|
||||
injected to the application classloader (`InstrumentationModule#getMuzzleHelperClassNames()`).
|
||||
Muzzle also automatically generates the `InstrumentationModule#registerMuzzleVirtualFields()`
|
||||
method.
|
||||
injected to the application classloader (`InstrumentationModuleMuzzle#getMuzzleHelperClassNames()`).
|
||||
Muzzle also automatically generates the `InstrumentationModuleMuzzle#registerMuzzleVirtualFields()`
|
||||
method. All collected references are then used to generate an `InstrumentationModuleMuzzle#getMuzzleReferences` method.
|
||||
|
||||
If you extend any of these `getMuzzle...()` methods in your `InstrumentationModule`, the muzzle
|
||||
compile plugin will not override your code: muzzle will only override those methods that do not have
|
||||
a custom implementation.
|
||||
If your `InstrumentationModule` subclass defines a method with exact same signature as a method
|
||||
from `InstrumentationModuleMuzzle`, the muzzle compile plugin will not override your code:
|
||||
muzzle will only generate those methods that do not have a custom implementation.
|
||||
|
||||
All collected references are then used to create a `ReferenceMatcher` instance. This matcher
|
||||
is stored in the instrumentation module class in the method `InstrumentationModule#getMuzzleReferenceMatcher()`
|
||||
and is shared between all type instrumentations. The bytecode of this method (basically an array of
|
||||
`Reference` builder calls) and the `getMuzzleHelperClassNames()` is generated automatically by the
|
||||
ByteBuddy plugin using an ASM code visitor.
|
||||
|
||||
The source code of the compile-time plugin is located in the `javaagent-tooling` module,
|
||||
package `io.opentelemetry.javaagent.muzzle.generation.collector`.
|
||||
The source code of the compile-time plugin is located in the `muzzle` module,
|
||||
package `io.opentelemetry.javaagent.tooling.muzzle.generation`.
|
||||
|
||||
### Runtime reference matching
|
||||
|
||||
The runtime reference matching process is implemented as a ByteBuddy matcher in `InstrumentationModule`.
|
||||
`MuzzleMatcher` uses the `getMuzzleReferenceMatcher()` method generated during the compilation phase
|
||||
`MuzzleMatcher` uses the `InstrumentationModuleMuzzle` methods generated during the compilation phase
|
||||
to verify that the class loader of the instrumented type has all necessary symbols (classes,
|
||||
methods, fields). If the `ReferenceMatcher` finds any mismatch between collected references and the
|
||||
methods, fields). If this matcher finds any mismatch between collected references and the
|
||||
actual application classpath types the whole instrumentation is discarded.
|
||||
|
||||
It is worth noting that because the muzzle check is expensive, it is only performed after a match
|
||||
|
@ -59,8 +55,7 @@ has been made by the `InstrumentationModule#classLoaderMatcher()` and `TypeInstr
|
|||
matchers. The result of muzzle matcher is cached per classloader, so that it is only executed
|
||||
once for the whole instrumentation module.
|
||||
|
||||
The source code of the runtime muzzle matcher is located in the `javaagent-tooling` module,
|
||||
in the class `Instrumenter.Default` and under the package `io.opentelemetry.javaagent.tooling.muzzle`.
|
||||
The source code of the runtime muzzle matcher is located in the `muzzle` module.
|
||||
|
||||
## Muzzle gradle plugin
|
||||
|
||||
|
|
|
@ -24,7 +24,6 @@ public class LambdaInstrumentationModule extends InstrumentationModule {
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getMuzzleHelperClassNames() {
|
||||
// this instrumentation uses ASM not ByteBuddy so muzzle doesn't automatically add helper
|
||||
// classes
|
||||
|
|
|
@ -49,7 +49,6 @@ public class MethodInstrumentationModule extends InstrumentationModule {
|
|||
|
||||
// the default configuration has empty "otel.instrumentation.methods.include", and so doesn't
|
||||
// generate any TypeInstrumentation for muzzle to analyze
|
||||
@Override
|
||||
public List<String> getMuzzleHelperClassNames() {
|
||||
return typeInstrumentations.isEmpty()
|
||||
? emptyList()
|
||||
|
|
|
@ -11,7 +11,6 @@ import static net.bytebuddy.matcher.ElementMatchers.any;
|
|||
import io.opentelemetry.instrumentation.api.config.Config;
|
||||
import io.opentelemetry.javaagent.extension.Ordered;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
@ -135,18 +134,4 @@ public abstract class InstrumentationModule implements Ordered {
|
|||
|
||||
/** Returns a list of all individual type instrumentation in this module. */
|
||||
public abstract List<TypeInstrumentation> typeInstrumentations();
|
||||
|
||||
/**
|
||||
* Returns a list of instrumentation helper classes, automatically detected by muzzle during
|
||||
* compilation. Those helpers will be injected into the application classloader.
|
||||
*
|
||||
* <p>The actual implementation of this method is generated automatically during compilation by
|
||||
* the {@code io.opentelemetry.instrumentation.javaagent-codegen} Gradle plugin.
|
||||
*
|
||||
* <p><b>This method is generated automatically</b>: if you override it, the muzzle compile plugin
|
||||
* will not generate a new implementation, it will leave the existing one.
|
||||
*/
|
||||
public List<String> getMuzzleHelperClassNames() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,7 +59,8 @@ public final class InstrumentationModuleInstaller {
|
|||
logger.debug("Instrumentation {} is disabled", instrumentationModule.instrumentationName());
|
||||
return parentAgentBuilder;
|
||||
}
|
||||
List<String> helperClassNames = instrumentationModule.getMuzzleHelperClassNames();
|
||||
List<String> helperClassNames =
|
||||
InstrumentationModuleMuzzle.getMuzzleHelperClassNames(instrumentationModule);
|
||||
HelperResourceBuilderImpl helperResourceBuilder = new HelperResourceBuilderImpl();
|
||||
instrumentationModule.registerHelperResources(helperResourceBuilder);
|
||||
List<TypeInstrumentation> typeInstrumentations = instrumentationModule.typeInstrumentations();
|
||||
|
|
|
@ -77,7 +77,8 @@ public class ClassLoaderMatcher {
|
|||
List<Mismatch> mismatches) {
|
||||
try {
|
||||
// verify helper injector works
|
||||
List<String> allHelperClasses = instrumentationModule.getMuzzleHelperClassNames();
|
||||
List<String> allHelperClasses =
|
||||
InstrumentationModuleMuzzle.getMuzzleHelperClassNames(instrumentationModule);
|
||||
HelperResourceBuilderImpl helperResourceBuilder = new HelperResourceBuilderImpl();
|
||||
instrumentationModule.registerHelperResources(helperResourceBuilder);
|
||||
if (!allHelperClasses.isEmpty()) {
|
||||
|
|
|
@ -7,6 +7,8 @@ package io.opentelemetry.javaagent.tooling.muzzle;
|
|||
|
||||
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
|
||||
import io.opentelemetry.javaagent.tooling.muzzle.references.ClassRef;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
|
@ -22,10 +24,34 @@ public interface InstrumentationModuleMuzzle {
|
|||
*/
|
||||
Map<String, ClassRef> getMuzzleReferences();
|
||||
|
||||
/** See {@link #getMuzzleReferences()}. */
|
||||
static Map<String, ClassRef> getMuzzleReferences(InstrumentationModule module) {
|
||||
if (module instanceof InstrumentationModuleMuzzle) {
|
||||
return ((InstrumentationModuleMuzzle) module).getMuzzleReferences();
|
||||
} else {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the associations between instrumented library classes and instrumentation context
|
||||
* classes. Keys (and their subclasses) will be associated with a context class stored in the
|
||||
* value.
|
||||
*/
|
||||
void registerMuzzleVirtualFields(VirtualFieldMappingsBuilder builder);
|
||||
|
||||
/**
|
||||
* Returns a list of instrumentation helper classes, automatically detected by muzzle during
|
||||
* compilation. Those helpers will be injected into the application classloader.
|
||||
*/
|
||||
List<String> getMuzzleHelperClassNames();
|
||||
|
||||
/** See {@link #getMuzzleHelperClassNames()}. */
|
||||
static List<String> getMuzzleHelperClassNames(InstrumentationModule module) {
|
||||
if (module instanceof InstrumentationModuleMuzzle) {
|
||||
return ((InstrumentationModuleMuzzle) module).getMuzzleHelperClassNames();
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -185,8 +185,8 @@ public abstract class Mismatch {
|
|||
}
|
||||
|
||||
/**
|
||||
* Represents failure to inject {@link InstrumentationModule#getMuzzleHelperClassNames()} into
|
||||
* some classloader.
|
||||
* Represents failure to inject {@link InstrumentationModuleMuzzle#getMuzzleHelperClassNames()}
|
||||
* into some classloader.
|
||||
*/
|
||||
public static class HelperClassesInjectionError extends Mismatch {
|
||||
|
||||
|
|
|
@ -1,66 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.tooling.muzzle;
|
||||
|
||||
import static java.util.Collections.emptyMap;
|
||||
|
||||
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
|
||||
import io.opentelemetry.javaagent.tooling.muzzle.references.ClassRef;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.Map;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* This class is used to access {@code getMuzzleReferences} method from the {@link
|
||||
* InstrumentationModule} class. That method is not visible in the source code and instead is
|
||||
* generated automatically during compilation by the {@code
|
||||
* io.opentelemetry.instrumentation.javaagent-codegen} Gradle plugin.
|
||||
*/
|
||||
class MuzzleReferencesAccessor {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(MuzzleReferencesAccessor.class);
|
||||
|
||||
private static final ClassValue<MethodHandle> getMuzzleReferences =
|
||||
new ClassValue<MethodHandle>() {
|
||||
@Override
|
||||
protected MethodHandle computeValue(Class<?> type) {
|
||||
MethodHandles.Lookup lookup = MethodHandles.publicLookup();
|
||||
MethodHandle handle;
|
||||
try {
|
||||
// This method is generated automatically during compilation by
|
||||
// the io.opentelemetry.instrumentation.javaagent-codegen Gradle plugin.
|
||||
handle =
|
||||
lookup.findVirtual(type, "getMuzzleReferences", MethodType.methodType(Map.class));
|
||||
} catch (NoSuchMethodException | IllegalAccessException e) {
|
||||
handle = null;
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns references to helper and library classes used in the given module's type
|
||||
* instrumentation advices, grouped by {@link ClassRef#getClassName()}.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
static Map<String, ClassRef> getFor(InstrumentationModule instrumentationModule) {
|
||||
if (instrumentationModule instanceof InstrumentationModuleMuzzle) {
|
||||
return ((InstrumentationModuleMuzzle) instrumentationModule).getMuzzleReferences();
|
||||
}
|
||||
|
||||
// Older classes created and compiled outside of this repo may not yet have the interface above.
|
||||
MethodHandle methodHandle = getMuzzleReferences.get(instrumentationModule.getClass());
|
||||
if (methodHandle != null) {
|
||||
logger.warn(
|
||||
"{} is compiled with old version of Muzzle and must be recompiled against newer version of OpenTelemetry Java Instrumentation APIs",
|
||||
instrumentationModule);
|
||||
}
|
||||
return emptyMap();
|
||||
}
|
||||
}
|
|
@ -39,8 +39,8 @@ public final class ReferenceMatcher {
|
|||
|
||||
public static ReferenceMatcher of(InstrumentationModule instrumentationModule) {
|
||||
return new ReferenceMatcher(
|
||||
instrumentationModule.getMuzzleHelperClassNames(),
|
||||
MuzzleReferencesAccessor.getFor(instrumentationModule),
|
||||
InstrumentationModuleMuzzle.getMuzzleHelperClassNames(instrumentationModule),
|
||||
InstrumentationModuleMuzzle.getMuzzleReferences(instrumentationModule),
|
||||
instrumentationModule::isHelperClass);
|
||||
}
|
||||
|
||||
|
|
|
@ -22,14 +22,15 @@ public final class ReferencesPrinter {
|
|||
/**
|
||||
* For all {@link InstrumentationModule}s found in the current thread's context classloader this
|
||||
* method prints references returned by the {@link
|
||||
* MuzzleReferencesAccessor#getFor(InstrumentationModule)} method to the standard output.
|
||||
* InstrumentationModuleMuzzle#getMuzzleReferences()} method to the standard output.
|
||||
*/
|
||||
public static void printMuzzleReferences() {
|
||||
for (InstrumentationModule instrumentationModule :
|
||||
ServiceLoader.load(InstrumentationModule.class)) {
|
||||
try {
|
||||
System.out.println(instrumentationModule.getClass().getName());
|
||||
for (ClassRef ref : MuzzleReferencesAccessor.getFor(instrumentationModule).values()) {
|
||||
for (ClassRef ref :
|
||||
InstrumentationModuleMuzzle.getMuzzleReferences(instrumentationModule).values()) {
|
||||
System.out.print(prettyPrint(ref));
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
|
|
Loading…
Reference in New Issue