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
|
The compile-time reference collection and code generation process is implemented using a ByteBuddy
|
||||||
plugin (called `MuzzleCodeGenerationPlugin`).
|
plugin (called `MuzzleCodeGenerationPlugin`).
|
||||||
|
|
||||||
For each instrumentation module the ByteBuddy plugin collects symbols referring to both internal and
|
For each instrumentation module the ByteBuddy plugin first applies `InstrumentationModuleMuzzle`
|
||||||
third party APIs used by the currently processed module's type
|
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
|
instrumentations (`InstrumentationModule#typeInstrumentations()`). The reference collection process
|
||||||
starts from advice classes (values of the map returned by the
|
starts from advice classes (values of the map returned by the
|
||||||
`TypeInstrumentation#transformers()` method) and traverses the class graph until it encounters a
|
`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 `InstrumentationModule#isHelperClass(String)` predicate). Aside from references,
|
||||||
the collection process also builds a graph of dependencies between internal instrumentation helper
|
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
|
classes - this dependency graph is later used to construct a list of helper classes that will be
|
||||||
injected to the application classloader (`InstrumentationModule#getMuzzleHelperClassNames()`).
|
injected to the application classloader (`InstrumentationModuleMuzzle#getMuzzleHelperClassNames()`).
|
||||||
Muzzle also automatically generates the `InstrumentationModule#registerMuzzleVirtualFields()`
|
Muzzle also automatically generates the `InstrumentationModuleMuzzle#registerMuzzleVirtualFields()`
|
||||||
method.
|
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
|
If your `InstrumentationModule` subclass defines a method with exact same signature as a method
|
||||||
compile plugin will not override your code: muzzle will only override those methods that do not have
|
from `InstrumentationModuleMuzzle`, the muzzle compile plugin will not override your code:
|
||||||
a custom implementation.
|
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
|
The source code of the compile-time plugin is located in the `muzzle` module,
|
||||||
is stored in the instrumentation module class in the method `InstrumentationModule#getMuzzleReferenceMatcher()`
|
package `io.opentelemetry.javaagent.tooling.muzzle.generation`.
|
||||||
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`.
|
|
||||||
|
|
||||||
### Runtime reference matching
|
### Runtime reference matching
|
||||||
|
|
||||||
The runtime reference matching process is implemented as a ByteBuddy matcher in `InstrumentationModule`.
|
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,
|
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.
|
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
|
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
|
matchers. The result of muzzle matcher is cached per classloader, so that it is only executed
|
||||||
once for the whole instrumentation module.
|
once for the whole instrumentation module.
|
||||||
|
|
||||||
The source code of the runtime muzzle matcher is located in the `javaagent-tooling` module,
|
The source code of the runtime muzzle matcher is located in the `muzzle` module.
|
||||||
in the class `Instrumenter.Default` and under the package `io.opentelemetry.javaagent.tooling.muzzle`.
|
|
||||||
|
|
||||||
## Muzzle gradle plugin
|
## Muzzle gradle plugin
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,6 @@ public class LambdaInstrumentationModule extends InstrumentationModule {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<String> getMuzzleHelperClassNames() {
|
public List<String> getMuzzleHelperClassNames() {
|
||||||
// this instrumentation uses ASM not ByteBuddy so muzzle doesn't automatically add helper
|
// this instrumentation uses ASM not ByteBuddy so muzzle doesn't automatically add helper
|
||||||
// classes
|
// classes
|
||||||
|
|
|
@ -49,7 +49,6 @@ public class MethodInstrumentationModule extends InstrumentationModule {
|
||||||
|
|
||||||
// the default configuration has empty "otel.instrumentation.methods.include", and so doesn't
|
// the default configuration has empty "otel.instrumentation.methods.include", and so doesn't
|
||||||
// generate any TypeInstrumentation for muzzle to analyze
|
// generate any TypeInstrumentation for muzzle to analyze
|
||||||
@Override
|
|
||||||
public List<String> getMuzzleHelperClassNames() {
|
public List<String> getMuzzleHelperClassNames() {
|
||||||
return typeInstrumentations.isEmpty()
|
return typeInstrumentations.isEmpty()
|
||||||
? emptyList()
|
? emptyList()
|
||||||
|
|
|
@ -11,7 +11,6 @@ import static net.bytebuddy.matcher.ElementMatchers.any;
|
||||||
import io.opentelemetry.instrumentation.api.config.Config;
|
import io.opentelemetry.instrumentation.api.config.Config;
|
||||||
import io.opentelemetry.javaagent.extension.Ordered;
|
import io.opentelemetry.javaagent.extension.Ordered;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
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. */
|
/** Returns a list of all individual type instrumentation in this module. */
|
||||||
public abstract List<TypeInstrumentation> typeInstrumentations();
|
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());
|
logger.debug("Instrumentation {} is disabled", instrumentationModule.instrumentationName());
|
||||||
return parentAgentBuilder;
|
return parentAgentBuilder;
|
||||||
}
|
}
|
||||||
List<String> helperClassNames = instrumentationModule.getMuzzleHelperClassNames();
|
List<String> helperClassNames =
|
||||||
|
InstrumentationModuleMuzzle.getMuzzleHelperClassNames(instrumentationModule);
|
||||||
HelperResourceBuilderImpl helperResourceBuilder = new HelperResourceBuilderImpl();
|
HelperResourceBuilderImpl helperResourceBuilder = new HelperResourceBuilderImpl();
|
||||||
instrumentationModule.registerHelperResources(helperResourceBuilder);
|
instrumentationModule.registerHelperResources(helperResourceBuilder);
|
||||||
List<TypeInstrumentation> typeInstrumentations = instrumentationModule.typeInstrumentations();
|
List<TypeInstrumentation> typeInstrumentations = instrumentationModule.typeInstrumentations();
|
||||||
|
|
|
@ -77,7 +77,8 @@ public class ClassLoaderMatcher {
|
||||||
List<Mismatch> mismatches) {
|
List<Mismatch> mismatches) {
|
||||||
try {
|
try {
|
||||||
// verify helper injector works
|
// verify helper injector works
|
||||||
List<String> allHelperClasses = instrumentationModule.getMuzzleHelperClassNames();
|
List<String> allHelperClasses =
|
||||||
|
InstrumentationModuleMuzzle.getMuzzleHelperClassNames(instrumentationModule);
|
||||||
HelperResourceBuilderImpl helperResourceBuilder = new HelperResourceBuilderImpl();
|
HelperResourceBuilderImpl helperResourceBuilder = new HelperResourceBuilderImpl();
|
||||||
instrumentationModule.registerHelperResources(helperResourceBuilder);
|
instrumentationModule.registerHelperResources(helperResourceBuilder);
|
||||||
if (!allHelperClasses.isEmpty()) {
|
if (!allHelperClasses.isEmpty()) {
|
||||||
|
|
|
@ -7,6 +7,8 @@ package io.opentelemetry.javaagent.tooling.muzzle;
|
||||||
|
|
||||||
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
|
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
|
||||||
import io.opentelemetry.javaagent.tooling.muzzle.references.ClassRef;
|
import io.opentelemetry.javaagent.tooling.muzzle.references.ClassRef;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -22,10 +24,34 @@ public interface InstrumentationModuleMuzzle {
|
||||||
*/
|
*/
|
||||||
Map<String, ClassRef> getMuzzleReferences();
|
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
|
* 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
|
* classes. Keys (and their subclasses) will be associated with a context class stored in the
|
||||||
* value.
|
* value.
|
||||||
*/
|
*/
|
||||||
void registerMuzzleVirtualFields(VirtualFieldMappingsBuilder builder);
|
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
|
* Represents failure to inject {@link InstrumentationModuleMuzzle#getMuzzleHelperClassNames()}
|
||||||
* some classloader.
|
* into some classloader.
|
||||||
*/
|
*/
|
||||||
public static class HelperClassesInjectionError extends Mismatch {
|
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) {
|
public static ReferenceMatcher of(InstrumentationModule instrumentationModule) {
|
||||||
return new ReferenceMatcher(
|
return new ReferenceMatcher(
|
||||||
instrumentationModule.getMuzzleHelperClassNames(),
|
InstrumentationModuleMuzzle.getMuzzleHelperClassNames(instrumentationModule),
|
||||||
MuzzleReferencesAccessor.getFor(instrumentationModule),
|
InstrumentationModuleMuzzle.getMuzzleReferences(instrumentationModule),
|
||||||
instrumentationModule::isHelperClass);
|
instrumentationModule::isHelperClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,14 +22,15 @@ public final class ReferencesPrinter {
|
||||||
/**
|
/**
|
||||||
* For all {@link InstrumentationModule}s found in the current thread's context classloader this
|
* For all {@link InstrumentationModule}s found in the current thread's context classloader this
|
||||||
* method prints references returned by the {@link
|
* 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() {
|
public static void printMuzzleReferences() {
|
||||||
for (InstrumentationModule instrumentationModule :
|
for (InstrumentationModule instrumentationModule :
|
||||||
ServiceLoader.load(InstrumentationModule.class)) {
|
ServiceLoader.load(InstrumentationModule.class)) {
|
||||||
try {
|
try {
|
||||||
System.out.println(instrumentationModule.getClass().getName());
|
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));
|
System.out.print(prettyPrint(ref));
|
||||||
}
|
}
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
|
|
Loading…
Reference in New Issue