Как избежать NoClassDefFoundError при использовании ByteBuddy для реализации OpenTracing - PullRequest
2 голосов
/ 08 июля 2019

Я пытаюсь реализовать агент Java, который можно легко добавить в командную строку некоторых из наших устаревших сервисов, чтобы включить распределенную трассировку с использованием OpenTracing API . Я использую ByteBuddy (1.9.12) и нажимаю NoClassDefFoundErrors, когда инструментированный код работает для следующих сценариев:

  1. инструктировал класс в пакете java.util.concurrent: не удалось найти GlobalTracer, поскольку агент Java был только в пути к классам приложения. Я временно обошел это, добавив Java-агент, включающий API-интерфейсы трассировки, в путь начальной загрузки класса в командной строке (только для Java 8), но я бы хотел сделать это программно. Ищите правильный способ сделать это. (Изменить: это решается с помощью параметра Boot-Class-Path в файле манифеста агента Java - просто укажите имя файла - без пути - самого JAR агента Java.)
  2. инструментировал пользовательский класс сетевого клиента: не удается найти класс в пользовательских сетевых пакетах из моего класса 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;
    }
}

1 Ответ

0 голосов
/ 09 июля 2019

Byte Buddy имеет адаптер совета для компоновщика агентов, который избегает загрузки классов и отражения, чтобы избежать ошибки, которую вы видите: AgentBuilder.Transformer.ForAdvice.

Этот преобразователь будет использовать TypePool для разрешения классов рекомендаций из загрузчика целевого класса и любого загрузчика классов, который вы добавляете для представления классов.

...