Я пытаюсь реализовать агент Java, который можно легко добавить в командную строку некоторых из наших устаревших сервисов, чтобы включить распределенную трассировку с использованием OpenTracing API . Я использую ByteBuddy
(1.9.12) и нажимаю NoClassDefFoundErrors
, когда инструментированный код работает для следующих сценариев:
- инструктировал класс в пакете
java.util.concurrent
: не удалось найти GlobalTracer
, поскольку агент Java был только в пути к классам приложения. Я временно обошел это, добавив Java-агент, включающий API-интерфейсы трассировки, в путь начальной загрузки класса в командной строке (только для Java 8), но я бы хотел сделать это программно. Ищите правильный способ сделать это. (Изменить: это решается с помощью параметра Boot-Class-Path
в файле манифеста агента Java - просто укажите имя файла - без пути - самого JAR агента Java.)
- инструментировал пользовательский класс сетевого клиента: не удается найти класс в пользовательских сетевых пакетах из моего класса Intercept (во время внедрения байтового кода), поскольку пакеты сетевого клиента отсутствуют в JAR агента Java. Я ищу «правильный способ» структурировать вещи на высоком уровне, чтобы избежать этого.
Код для сценария 2:
public class NettyAgentRule implements AgentRule {
public Iterable<? extends AgentBuilder> buildAgent(AgentBuilder agentBuilder) {
return Arrays.asList(agentBuilder
.type(hasSuperType(named("org.jboss.netty.bootstrap.Bootstrap")))
.transform((builder, typeDescription, classLoader, module) -> {
return builder.visit(Advice.to(NettyAgentRule.class).on(named("setPipelineFactory")));
}));
}
@Advice.OnMethodEnter
public static void enter(final @Advice.Origin String origin,
final @Advice.This Object thiz,
@Advice.Argument(value = 0, readOnly = false, typing = Assigner.Typing.DYNAMIC) Object parameter) {
parameter = NettyAgentIntercept.enter(thiz, parameter);
}
}
где Ошибка происходит в классе NettyAgentIntercept, который ссылается на классы Netty.
Я исследовал, как OpenTracing 'contrib' SpecialAgent обрабатывает эти сценарии, и у него довольно много пользовательских загрузок классов, которые тесно связаны с их структурой сборки (JAR в JAR в соответствии с соглашением об именах). Этого было бы неплохо избежать, если это возможно.
Пример стека исключений из сценария 2:
java.lang.NoClassDefFoundError: org/jboss/netty/channel/ChannelPipelineFactory
2019-07-05 18:52:21,10613 at com.example.tracing.netty.NettyAgentIntercept.enter(NettyAgentIntercept.java:9)
2019-07-05 18:52:21,10613 at org.jboss.netty.bootstrap.Bootstrap.setPipelineFactory(Bootstrap.java:251)
2019-07-05 18:52:21,10616 at com.example.lib.util.net.NettyCore.<init>(NettyCore.java:176)
2019-07-05 18:52:21,10616 at com.example.lib.util.net.NetClient.getInstance(NetClient.java:125)
2019-07-05 18:52:21,10617 at com.example.lib.util.net.NetClient.getInstance(NetClient.java:147)
2019-07-05 18:52:21,10617 at com.example.lib.util.net.NetClient.getInstance(NetClient.java:55)
2019-07-05 18:52:21,10617 at com.example.component.Component.main(Component.java:155)
2019-07-05 18:52:21,10618 at com.example.lib.util.invoke.ComponentThread.run(ComponentThread.java:21)
Я также попробовал следующий код, как рекомендовано в
Байт-приятель выбрасывает java.lang.ClassNotFoundException: javax.servlet.http.HttpServlet
но это приводит к тому же виду исключения:
public Iterable<? extends AgentBuilder> buildAgent(AgentBuilder agentBuilder) {
return Arrays.asList(agentBuilder
.type(hasSuperType(named("org.jboss.netty.bootstrap.Bootstrap")))
.transform(new AgentBuilder.Transformer.ForAdvice()
.include(getClass().getClassLoader())
.advice(named("setPipelineFactory"),"com.example.tracing.netty.NettyAgentAdvice")
));
}
, где класс консультации
package com.example.tracing.netty;
import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.implementation.bytecode.assign.Assigner;
public class NettyAgentAdvice {
@Advice.OnMethodEnter
public static void enter(final @Advice.Origin String origin,
final @Advice.This Object thiz,
@Advice.Argument(value = 0, readOnly = false, typing = Assigner.Typing.DYNAMIC) Object parameter) {
parameter = enter(thiz, parameter);
}
public static ChannelPipelineFactory enter(Object thiz, Object returned) {
ChannelPipelineFactory pipelineFactory = (ChannelPipelineFactory) returned;
if (thiz instanceof ClientBootstrap) {
return () -> {
ChannelPipeline pipeline = pipelineFactory.getPipeline();
if (pipeline.get(TracingHandler.class) == null) {
pipeline.addLast("tracing", new TracingHandler());
}
return pipeline;
};
}
return pipelineFactory;
}
}