Add an option to mark indy converted instrumentations (#13665)
This commit is contained in:
parent
4855626d53
commit
818f73f5fb
|
@ -10,10 +10,12 @@ import static java.util.Arrays.asList;
|
||||||
import com.google.auto.service.AutoService;
|
import com.google.auto.service.AutoService;
|
||||||
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
|
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
|
||||||
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
|
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
|
||||||
|
import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@AutoService(InstrumentationModule.class)
|
@AutoService(InstrumentationModule.class)
|
||||||
public class PekkoHttpClientInstrumentationModule extends InstrumentationModule {
|
public class PekkoHttpClientInstrumentationModule extends InstrumentationModule
|
||||||
|
implements ExperimentalInstrumentationModule {
|
||||||
public PekkoHttpClientInstrumentationModule() {
|
public PekkoHttpClientInstrumentationModule() {
|
||||||
super("pekko-http", "pekko-http-1.0", "pekko-http-client");
|
super("pekko-http", "pekko-http-1.0", "pekko-http-client");
|
||||||
}
|
}
|
||||||
|
@ -22,4 +24,9 @@ public class PekkoHttpClientInstrumentationModule extends InstrumentationModule
|
||||||
public List<TypeInstrumentation> typeInstrumentations() {
|
public List<TypeInstrumentation> typeInstrumentations() {
|
||||||
return asList(new HttpExtClientInstrumentation(), new PoolMasterActorInstrumentation());
|
return asList(new HttpExtClientInstrumentation(), new PoolMasterActorInstrumentation());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isIndyReady() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,4 +75,14 @@ public interface ExperimentalInstrumentationModule {
|
||||||
default Map<JavaModule, List<String>> jpmsModulesToOpen() {
|
default Map<JavaModule, List<String>> jpmsModulesToOpen() {
|
||||||
return Collections.emptyMap();
|
return Collections.emptyMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signals that the advice in this module is ready to be used with indy instrumentation and the
|
||||||
|
* automatic advice conversion doesn't need to be applied.
|
||||||
|
*
|
||||||
|
* @return true if module is ready to be used with indy instrumentation.
|
||||||
|
*/
|
||||||
|
default boolean isIndyReady() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,219 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.tooling.instrumentation.indy;
|
||||||
|
|
||||||
|
import static io.opentelemetry.javaagent.tooling.instrumentation.indy.ForceDynamicallyTypedAssignReturnedFactory.replaceAnnotationValue;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import net.bytebuddy.agent.builder.AgentBuilder;
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
import net.bytebuddy.description.annotation.AnnotationDescription;
|
||||||
|
import net.bytebuddy.description.annotation.AnnotationList;
|
||||||
|
import net.bytebuddy.description.annotation.AnnotationValue;
|
||||||
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
import net.bytebuddy.description.method.MethodList;
|
||||||
|
import net.bytebuddy.description.method.ParameterDescription;
|
||||||
|
import net.bytebuddy.description.method.ParameterList;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.description.type.TypeList;
|
||||||
|
import net.bytebuddy.dynamic.ClassFileLocator;
|
||||||
|
import net.bytebuddy.pool.TypePool;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pool strategy that sets "inline" attribute to false on {@link Advice.OnMethodEnter} and {@link
|
||||||
|
* Advice.OnMethodExit} annotations.
|
||||||
|
*/
|
||||||
|
class AdviceUninliningPoolStrategy implements AgentBuilder.PoolStrategy {
|
||||||
|
private final AgentBuilder.PoolStrategy poolStrategy;
|
||||||
|
|
||||||
|
public AdviceUninliningPoolStrategy(AgentBuilder.PoolStrategy poolStrategy) {
|
||||||
|
this.poolStrategy = poolStrategy;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public TypePool typePool(@NotNull ClassFileLocator classFileLocator, ClassLoader classLoader) {
|
||||||
|
TypePool typePool = poolStrategy.typePool(classFileLocator, classLoader);
|
||||||
|
return new TypePoolWrapper(typePool);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public TypePool typePool(
|
||||||
|
@NotNull ClassFileLocator classFileLocator, ClassLoader classLoader, @NotNull String name) {
|
||||||
|
TypePool typePool = poolStrategy.typePool(classFileLocator, classLoader, name);
|
||||||
|
return new TypePoolWrapper(typePool);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class TypePoolWrapper implements TypePool {
|
||||||
|
private final TypePool typePool;
|
||||||
|
|
||||||
|
public TypePoolWrapper(TypePool typePool) {
|
||||||
|
this.typePool = typePool;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Resolution describe(@NotNull String name) {
|
||||||
|
Resolution resolution = typePool.describe(name);
|
||||||
|
|
||||||
|
return new Resolution() {
|
||||||
|
@Override
|
||||||
|
public boolean isResolved() {
|
||||||
|
return resolution.isResolved();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public TypeDescription resolve() {
|
||||||
|
TypeDescription typeDescription = resolution.resolve();
|
||||||
|
|
||||||
|
return new TypeDescription.AbstractBase.OfSimpleType.WithDelegation() {
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return typeDescription.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
protected TypeDescription delegate() {
|
||||||
|
return typeDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public MethodList<MethodDescription.InDefinedShape> getDeclaredMethods() {
|
||||||
|
MethodList<MethodDescription.InDefinedShape> methods = super.getDeclaredMethods();
|
||||||
|
|
||||||
|
class MethodListWrapper
|
||||||
|
extends MethodList.AbstractBase<MethodDescription.InDefinedShape> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MethodDescription.InDefinedShape get(int index) {
|
||||||
|
return new MethodDescriptionWrapper(methods.get(index));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return methods.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new MethodListWrapper();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
typePool.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class MethodDescriptionWrapper extends DelegatingMethodDescription {
|
||||||
|
|
||||||
|
MethodDescriptionWrapper(MethodDescription.InDefinedShape method) {
|
||||||
|
super(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public AnnotationList getDeclaredAnnotations() {
|
||||||
|
AnnotationList annotations = method.getDeclaredAnnotations();
|
||||||
|
|
||||||
|
class AnnotationListWrapper extends AnnotationList.AbstractBase {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AnnotationDescription get(int index) {
|
||||||
|
AnnotationDescription annotation = annotations.get(index);
|
||||||
|
String annotationTypeName = annotation.getAnnotationType().getActualName();
|
||||||
|
// we are only interested in OnMethodEnter and OnMethodExit annotations
|
||||||
|
if (!Advice.OnMethodEnter.class.getName().equals(annotationTypeName)
|
||||||
|
&& !Advice.OnMethodExit.class.getName().equals(annotationTypeName)) {
|
||||||
|
return annotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
// replace value for "inline" attribute with false
|
||||||
|
return replaceAnnotationValue(
|
||||||
|
annotation, "inline", oldVal -> AnnotationValue.ForConstant.of(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return annotations.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new AnnotationListWrapper();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class DelegatingMethodDescription
|
||||||
|
extends MethodDescription.InDefinedShape.AbstractBase {
|
||||||
|
protected final MethodDescription.InDefinedShape method;
|
||||||
|
|
||||||
|
DelegatingMethodDescription(MethodDescription.InDefinedShape method) {
|
||||||
|
this.method = method;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public TypeDescription getDeclaringType() {
|
||||||
|
return method.getDeclaringType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public TypeDescription.Generic getReturnType() {
|
||||||
|
return method.getReturnType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public ParameterList<ParameterDescription.InDefinedShape> getParameters() {
|
||||||
|
return method.getParameters();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public TypeList.Generic getExceptionTypes() {
|
||||||
|
return method.getExceptionTypes();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AnnotationValue<?, ?> getDefaultValue() {
|
||||||
|
return method.getDefaultValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public String getInternalName() {
|
||||||
|
return method.getInternalName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public TypeList.Generic getTypeVariables() {
|
||||||
|
return method.getTypeVariables();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getModifiers() {
|
||||||
|
return method.getModifiers();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public AnnotationList getDeclaredAnnotations() {
|
||||||
|
return method.getDeclaredAnnotations();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -68,7 +68,6 @@ public class ForceDynamicallyTypedAssignReturnedFactory implements Advice.PostPr
|
||||||
}
|
}
|
||||||
|
|
||||||
private static AnnotationDescription forceDynamicTyping(AnnotationDescription anno) {
|
private static AnnotationDescription forceDynamicTyping(AnnotationDescription anno) {
|
||||||
|
|
||||||
String name = anno.getAnnotationType().getName();
|
String name = anno.getAnnotationType().getName();
|
||||||
if (name.equals(TO_FIELD_TYPENAME)
|
if (name.equals(TO_FIELD_TYPENAME)
|
||||||
|| name.equals(TO_ARGUMENT_TYPENAME)
|
|| name.equals(TO_ARGUMENT_TYPENAME)
|
||||||
|
@ -101,7 +100,7 @@ public class ForceDynamicallyTypedAssignReturnedFactory implements Advice.PostPr
|
||||||
return anno;
|
return anno;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static AnnotationDescription replaceAnnotationValue(
|
static AnnotationDescription replaceAnnotationValue(
|
||||||
AnnotationDescription anno,
|
AnnotationDescription anno,
|
||||||
String propertyName,
|
String propertyName,
|
||||||
Function<AnnotationValue<?, ?>, AnnotationValue<?, ?>> valueMapper) {
|
Function<AnnotationValue<?, ?>, AnnotationValue<?, ?>> valueMapper) {
|
||||||
|
|
|
@ -7,6 +7,7 @@ package io.opentelemetry.javaagent.tooling.instrumentation.indy;
|
||||||
|
|
||||||
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
|
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
|
||||||
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
|
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
|
||||||
|
import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule;
|
||||||
import io.opentelemetry.javaagent.tooling.bytebuddy.ExceptionHandlers;
|
import io.opentelemetry.javaagent.tooling.bytebuddy.ExceptionHandlers;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -25,11 +26,15 @@ public final class IndyTypeTransformerImpl implements TypeTransformer {
|
||||||
private final Advice.WithCustomMapping adviceMapping;
|
private final Advice.WithCustomMapping adviceMapping;
|
||||||
private AgentBuilder.Identified.Extendable agentBuilder;
|
private AgentBuilder.Identified.Extendable agentBuilder;
|
||||||
private final InstrumentationModule instrumentationModule;
|
private final InstrumentationModule instrumentationModule;
|
||||||
|
private final boolean transformAdvice;
|
||||||
|
|
||||||
public IndyTypeTransformerImpl(
|
public IndyTypeTransformerImpl(
|
||||||
AgentBuilder.Identified.Extendable agentBuilder, InstrumentationModule module) {
|
AgentBuilder.Identified.Extendable agentBuilder, InstrumentationModule module) {
|
||||||
this.agentBuilder = agentBuilder;
|
this.agentBuilder = agentBuilder;
|
||||||
this.instrumentationModule = module;
|
this.instrumentationModule = module;
|
||||||
|
this.transformAdvice =
|
||||||
|
!(instrumentationModule instanceof ExperimentalInstrumentationModule)
|
||||||
|
|| !((ExperimentalInstrumentationModule) instrumentationModule).isIndyReady();
|
||||||
this.adviceMapping =
|
this.adviceMapping =
|
||||||
Advice.withCustomMapping()
|
Advice.withCustomMapping()
|
||||||
.with(
|
.with(
|
||||||
|
@ -44,17 +49,23 @@ public final class IndyTypeTransformerImpl implements TypeTransformer {
|
||||||
@Override
|
@Override
|
||||||
public void applyAdviceToMethod(
|
public void applyAdviceToMethod(
|
||||||
ElementMatcher<? super MethodDescription> methodMatcher, String adviceClassName) {
|
ElementMatcher<? super MethodDescription> methodMatcher, String adviceClassName) {
|
||||||
|
// default strategy used by AgentBuilder.Transformer.ForAdvice
|
||||||
|
AgentBuilder.PoolStrategy poolStrategy = AgentBuilder.PoolStrategy.Default.FAST;
|
||||||
|
|
||||||
agentBuilder =
|
agentBuilder =
|
||||||
agentBuilder.transform(
|
agentBuilder.transform(
|
||||||
new AgentBuilder.Transformer.ForAdvice(adviceMapping)
|
new AgentBuilder.Transformer.ForAdvice(adviceMapping)
|
||||||
.advice(methodMatcher, adviceClassName)
|
.advice(methodMatcher, adviceClassName)
|
||||||
|
// advice transformation already performs uninlining
|
||||||
|
.with(
|
||||||
|
transformAdvice ? poolStrategy : new AdviceUninliningPoolStrategy(poolStrategy))
|
||||||
.include(getAdviceLocator(instrumentationModule.getClass().getClassLoader()))
|
.include(getAdviceLocator(instrumentationModule.getClass().getClassLoader()))
|
||||||
.withExceptionHandler(ExceptionHandlers.defaultExceptionHandler()));
|
.withExceptionHandler(ExceptionHandlers.defaultExceptionHandler()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ClassFileLocator getAdviceLocator(ClassLoader classLoader) {
|
private ClassFileLocator getAdviceLocator(ClassLoader classLoader) {
|
||||||
ClassFileLocator classFileLocator = ClassFileLocator.ForClassLoader.of(classLoader);
|
ClassFileLocator classFileLocator = ClassFileLocator.ForClassLoader.of(classLoader);
|
||||||
return new AdviceLocator(classFileLocator);
|
return transformAdvice ? new AdviceLocator(classFileLocator) : classFileLocator;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
Loading…
Reference in New Issue