arextest / arex-agent-java

Lightweight Java agent for traffic capture and replay, enhancing testing and debugging.
https://doc.arextest.com/
Apache License 2.0
440 stars 89 forks source link

[Bug] agent录制结果为null #553

Open freedom-zhishui opened 1 month ago

freedom-zhishui commented 1 month ago

Search before asking

AREX Test Service

AREX Java Agent (arextest/arex-agent-java)

Current Behavior

你好! 描述:在录制时,子调用未录取到值返回值为Null,导致上层接口返回值也为Null 猜测:发生远程调用是公司人员内部封装了restTemplate和OKHttp等 具体代码: 代码入口1: // service-url.menu.validate 实际是一个http://地址 @RestClient(url = "${service-url.menu.validate}") public interface MenuValidatePreOrder extends MenuValidateClient{

@Override
@PostMapping(value = "epl")
ProductRes getProductBetter(@RequestBody StoreVo storeVo);
}

代码入口2: @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface RestClient { String name() default "";

String url() default "";

String path() default "";

}

代码入口3: @Configuration public class RestClientConfiguration implements BeanDefinitionRegistryPostProcessor { private static final Logger LOGGER = LoggerFactory.getLogger(RestClientConfiguration.class);

public RestClientConfiguration() {
}

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
    Environment environment = (Environment)((DefaultListableBeanFactory)registry).getBean("environment");
    String packagePrefix = environment.getProperty("cloud.vo.package.prefix", "com.cloud.vo3");
    Reflections reflections = new Reflections(packagePrefix, new Scanner[0]);
    Set<Class<?>> restClientClassSet = reflections.getTypesAnnotatedWith(RestClient.class);
    Iterator var6 = restClientClassSet.iterator();

    AbstractBeanDefinition beanDefinition;
    while(var6.hasNext()) {
        Class<?> restClientClass = (Class)var6.next();
        if (restClientClass.isInterface()) {
            Object restClient = restClientClass.cast(Proxy.newProxyInstance(restClientClass.getClassLoader(), new Class[]{restClientClass}, new RestClientHandler()));
            BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(restClient.getClass());
            definition.addConstructorArgValue(new RestClientHandler());
            definition.setAutowireMode(1);
            beanDefinition = definition.getBeanDefinition();
            BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, this.getBeanName(restClientClass), new String[0]);
            BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
        }
    }

    reflections = new Reflections(packagePrefix, new Scanner[]{new MethodAnnotationsScanner()});
    Set<Method> methodSet = reflections.getMethodsAnnotatedWith(CustomRestTemplate.class);
    Iterator var13 = methodSet.iterator();

    while(var13.hasNext()) {
        Method method = (Method)var13.next();
        String restTemplateName = ((CustomRestTemplate)method.getAnnotation(CustomRestTemplate.class)).value();
        if (!RestTemplateFactory.restTemplateRepository.containsKey(restTemplateName)) {
            beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
            beanDefinition.setBeanClass(RestTemplate.class);
            registry.registerBeanDefinition(restTemplateName, beanDefinition);
            RestTemplateFactory.restTemplateRepository.put(restTemplateName, (Object)null);
        }
    }

    String restTemplateName = "urlRestTemplate";
    AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
    beanDefinition.setBeanClass(RestTemplate.class);
    registry.registerBeanDefinition(restTemplateName, beanDefinition);
    RestTemplateFactory.restTemplateRepository.put(restTemplateName, (Object)null);
}

代码入口4: @Configuration public class RestTemplateConfiguration { private static final Logger LOGGER = LoggerFactory.getLogger(RestTemplateConfiguration.class); private static final String CONNECT_TIMEOUT_KEY = "restClient.%s.connectTimeout"; private static final String READ_TIMEOUT_KEY = "restClient.%s.readTimeout"; private static final String USE_OK_HTTP_KEY = "restClient.%s.useOkhttp"; @Autowired private HttpClient httpClient; @Autowired private OkHttpClient okHttpClient; @Autowired private Environment environment; @Autowired( required = false ) private PrintReqResponseLog4ClientInterceptor printReqResponseLog4ClientInterceptor; @Autowired( required = false ) private AddTraceInfoHeader4ClientInterceptor addTraceInfoHeader4ClientInterceptor;

public RestTemplateConfiguration() {
}

@Bean
public RestTemplate restTemplate(@Autowired RestTemplate restTemplate) {
    LOGGER.info("初始化内部服务调用的RestTemplate");
    int connectTimeout = this.properties.getConnectTimeout().intValue() * 1000;
    int readTimeout = this.properties.getReadTimeout().intValue() * 1000;
    boolean useOkhttp = Boolean.parseBoolean(this.environment.getProperty(String.format("restClient.%s.useOkhttp", "restTemplate"), "false"));
    if (useOkhttp) {
        restTemplate.setRequestFactory(this.okHttpRequestFactory(this.okHttpClient, connectTimeout, readTimeout));
    } else {
        restTemplate.setRequestFactory(this.httpClientRequestFactory(this.httpClient, connectTimeout, readTimeout));
    }

    restTemplate.setErrorHandler(new RestResponseErrorHandler());
    this.changeMessageConverter(restTemplate);
    return restTemplate;
}

Expected Behavior

您好! 我的预期行为是在Anything else 可以录制到子进程

Steps To Reproduce

您好! 1、在当前行为中,我复制了一些项目中自定义封装的远程调用,我想请问下,处于这种情况,我们arex-agent是不是就是会录制不到自调用的信息。 2、我在社区看到arex-agent不支持公司内部自定义封装的开源插件,想我这种情况,是不是就是不支持,需要开发针对于目标项目适配的插件

3、我将图片贴到Anything else 里了

Anything else

image image

Are you willing to submit a pull request to fix on your own?

lucas-myx commented 1 month ago

@freedom-zhishui 感谢反馈 restTemplate和okhttp都是支持录制的,而且你的截图显示的是结果为null,并不是没有录制,如果没有录制下来就不会有这条记录,从现象看是只录制了请求参数,响应未录制,这种情况可能是结果无法序列化导致的,可以查下应用的日志信息,如果方便也可以直接在QQ群(656108079)里沟通,谢谢!

freedom-zhishui commented 1 month ago

你好,lucas!我项目是用注解标记需要做的远程调用的类,然后再使用解析注解的类来做远程的调用,用的是restTemplate和OKHttp,这样不影响arex的录制是吗。我测试了使用feign,OkHttpClient直接调用远程,是可以拿到录制的数据的。只有通过公司内部封装的远程调用当时录制的值为null(封装的底层使用的restTemplate,HttpClient和OkHttpClent)。 期望解答,谢谢

发自我的iPhone

------------------ 原始邮件 ------------------ 发件人: Lucas @.> 发送时间: 2024年7月31日 17:46 收件人: arextest/arex-agent-java @.> 抄送: 钱自翔 @.>, Mention @.> 主题: Re: [arextest/arex-agent-java] [Bug] agent录制结果为null (Issue #553)