Cannot resolve bytebuddy otel class when using a second Java Agent with ByteBuddy for Kafka instrumentation
I'm encountering a net.bytebuddy.pool.TypePool$Resolution$NoSuchTypeException when using a custom Java Agent based on ByteBuddy in conjunction with the OpenTelemetry Java Agent. The error occurs during transformation of KafkaConsumer:
Failed to handle org.apache.kafka.clients.consumer.KafkaConsumer for transformation on class loader org.springframework.boot.loader.LaunchedURLClassLoader@395065af net.bytebuddy.pool.TypePool$Resolution$NoSuchTypeException: Cannot resolve type description for io.opentelemetry.javaagent.bootstrap.field.VirtualFieldAccessor$org$apache$kafka$clients$consumer$Consumer$java$util$Map
at shade.dependencies.pool.TypePool$Resolution$Illegal.resolve(TypePool.java:192)
at shade.dependencies.pool.TypePool$Default$WithLazyResolution$LazyTypeDescription.delegate(TypePool.java:1170)
at shade.dependencies.description.type.TypeDescription$AbstractBase$OfSimpleType$WithDelegation.getTypeVariables(TypeDescription.java:8532)
at shade.dependencies.description.type.TypeDescription$AbstractBase.isGenerified(TypeDescription.java:8138)
at shade.dependencies.description.type.TypeDescription$Generic$Visitor$Reifying.onNonGenericType(TypeDescription.java:1734)
at shade.dependencies.description.type.TypeDescription$Generic$Visitor$Reifying$1.onNonGenericType(TypeDescription.java:1690)
at shade.dependencies.description.type.TypeDescription$Generic$OfNonGenericType.accept(TypeDescription.java:3749)
at shade.dependencies.dynamic.scaffold.MethodGraph$Compiler$Default.doAnalyze(MethodGraph.java:746)
at shade.dependencies.dynamic.scaffold.MethodGraph$Compiler$Default.compile(MethodGraph.java:668)
at shade.dependencies.dynamic.scaffold.MethodGraph$Compiler$AbstractBase.compile(MethodGraph.java:519)
at shade.dependencies.dynamic.scaffold.MethodRegistry$Default.prepare(MethodRegistry.java:472)
at shade.dependencies.dynamic.scaffold.inline.RedefinitionDynamicTypeBuilder.toTypeWriter(RedefinitionDynamicTypeBuilder.java:213)
at shade.dependencies.dynamic.DynamicType$Builder$AbstractBase$UsingTypeWriter.make(DynamicType.java:4064)
at shade.dependencies.agent.builder.AgentBuilder$Default$ExecutingTransformer.doTransform(AgentBuilder.java:12620)
at shade.dependencies.agent.builder.AgentBuilder$Default$ExecutingTransformer.transform(AgentBuilder.java:12555)
at shade.dependencies.agent.builder.AgentBuilder$Default$ExecutingTransformer.access$1800(AgentBuilder.java:12264)
at shade.dependencies.agent.builder.AgentBuilder$Default$ExecutingTransformer$Java9CapableVmDispatcher.run(AgentBuilder.java:13046)
at shade.dependencies.agent.builder.AgentBuilder$Default$ExecutingTransformer$Java9CapableVmDispatcher.run(AgentBuilder.java:12976)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
at shade.dependencies.agent.builder.AgentBuilder$Default$ExecutingTransformer.doPrivileged(AgentBuilder.java)
at shade.dependencies.agent.builder.AgentBuilder$Default$ExecutingTransformer.transform(AgentBuilder.java:12498)
at shade.dependencies.agent.builder.AgentBuilder$Default$ExecutingTransformer$ByteBuddy$ModuleSupport.transform(Unknown Source)
It seems that my second agent is unable to resolve the VirtualFieldAccessor class injected by the OpenTelemetry agent
Important constraint My custom agent must be loaded last, due to architectural requirements and lifecycle constraints.
Expected behavior My second agent should be able to safely apply transformations to Kafka classes (e.g., Consumer) without encountering unresolved type exceptions related to OpenTelemetry's internal virtual field mechanism.
Request for guidance Is there a recommended way to access or safely bypass OpenTelemetry-injected types (e.g., VirtualFieldAccessor) in a multi-agent setup? Is it possible to avoid interference with OpenTelemetry's instrumentation when we need to augment the same target class?
my bytebuddy bulder is:
new Default().disableClassFormatChanges().installOn(instrumentation)
The OpenTelemetry agent likely appends the boot loader with additional types, and those types cannot be resolved by a class loader as their class file is not available. Basically, you only have two options:
- You add a resolver for a
ClassFileLocatororTypePoolthat also looks up the classes from the other agent. - You use a resolution strategy that does not imply a full resolution of type descriptions. For example, you could decorate classes, what however limits your own capability to add fields or methods.
What kind if transformations are you applying?
I referred to https://github.com/raphw/byte-buddy/issues/1419, and that resolved the issue.
public MethodGraph.Linked compile(TypeDefinition typeDefinition, TypeDescription viewPoint) {
for (MethodDescription methodDescription : typeDefinition.getDeclaredMethods()
.filter(
// ignores all methods which refer to unknown types
failSafe(
isVirtual()
.and(not(isBridge()))
.and(isVisibleTo(viewPoint))
.and(hasParameters(whereNone(hasType(not(isVisibleTo(viewPoint))))))
)
)
) {
//todo something
}
return new MethodGraph.Linked.Delegation(.....);
}