MethodDelegation's @Argument is working?
Hi, thank you for the maintenance of this project. This project is so helpful for me.
I'd like to get Spring app's http request information by javaagent, and write some code like this repository. https://github.com/hi120ki/spring-javaagent-test
- Spring app
@RestController
public class HelloController {
@GetMapping("/")
public String index() {
return "Greetings from Spring Boot!";
}
}
- Agent
public class Agent {
public static void premain(String agentArgs, Instrumentation inst) {
System.out.println("agentArgs : " + agentArgs);
new AgentBuilder.Default()
.type(
ElementMatchers.named(
"org.springframework.web.servlet.DispatcherServlet"))
.transform((builder, type, classLoader, module) -> builder
.method(ElementMatchers.named("doDispatch"))
.intercept(MethodDelegation.to(InstrumentInterceptor.class)))
.installOn(inst);
}
}
- InstrumentInterceptor
public class InstrumentInterceptor {
@RuntimeType
public static Object intercept(
@Argument(0) HttpServletRequest request,
@SuperCall Callable<?> callable) {
System.out.println(request.getMethod());
try {
return callable.call();
} catch (Exception e) {
System.out.println("Exception :" + e.getMessage());
return null;
} finally {
System.out.println(request.getRequestURI());
}
}
}
I found org.springframework.web.servlet.DispatcherServlet.doDispatch 's first argument is javax.servlet.http.HttpServletRequest, and I tried to pick-up some HttpServletRequest properties.
But these code didn't work. (I can get http response by curl command, but no javaagent's stdout in spring app.)
Could you give me some comments?
Thank you.
A servlet gets the response as a second argument. Did you try that after the super call? You can inject it as another annotated parameter.
Thank you for comment.
I try this code, but no stdout.
public class InstrumentInterceptor {
@RuntimeType
public static Object intercept(
@Argument(0) HttpServletRequest request,
@Argument(1) HttpServletResponse response,
@SuperCall Callable<?> callable) {
System.out.println("[1] request : " + request.getMethod());
try {
System.out.println("[2] request : " + request.getMethod());
Object ret = callable.call();
System.out.println("[3] request : " + request.getMethod());
System.out.println("[1] response : " + response.getContentType());
return ret;
} catch (Exception e) {
System.out.println("Exception :" + e.getMessage());
return null;
} finally {
System.out.println("[4] request : " + request.getMethod());
}
}
}
For MethodDelegation.to approach, we can use other annotations like @SuperCall Callable<?> callable, @Origin Method method , but @Argument doesn't work.
We can think of two reasons for these, first is there is no information in the method to get, and second is @Argument is not working.
I'm trying to intercept other method that argument is HttpServletRequest, but it doesn't work.
Did you import the right annotation? It must not be from the advice package. If none of the code is called there must be an error during interception which should show in the logs.
Yes, this code import net.bytebuddy.implementation.bind.annotation.Argument;
https://github.com/hi120ki/spring-javaagent-test/blob/main/agent/src/main/java/com/example/profile/InstrumentInterceptor.java#L6
package com.example.profile;
import java.util.concurrent.Callable;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.bytebuddy.implementation.bind.annotation.Argument;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.SuperCall;
public class InstrumentInterceptor {
@RuntimeType
public static Object intercept(
@Argument(0) HttpServletRequest request,
@Argument(1) HttpServletResponse response,
@SuperCall Callable<?> callable) {
System.out.println("[ShowMethod method1] " + request.getMethod());
try {
System.out.println("[ShowMethod method1] " + request.getMethod());
Object ret = callable.call();
System.out.println("[ShowMethod method1] " + request.getMethod());
return ret;
} catch (Exception e) {
System.out.println("[ShowMethod Exception] " + e.getMessage());
return null;
} finally {
System.out.println("[ShowMethod method1] " + request.getMethod());
}
}
}
What do you mean by "does not work" here, though? Looks right to me.
This code try to print HttpServletRequest's property, and If it works, the output should be like this
[ShowMethod method1] GET
but there is no stdout and no error output.
Did you register an AgentBuilder.Listener? It should print out any class that is instrumented or ignored.
Thank you.
I add .with(AgentBuilder.Listener.StreamWriting.toSystemError().withErrorsOnly()) to new AgentBuilder.Default(), then got an error message.
[Byte Buddy] ERROR org.springframework.web.servlet.DispatcherServlet [org.springframework.boot.loader.LaunchedURLClassLoader@72e5a8e, unnamed module @f68f0dc, Thread[main,5,main], loaded=false]
java.lang.NoClassDefFoundError: javax/servlet/http/HttpServletRequest
at java.base/java.lang.Class.getDeclaredMethods0(Native Method)
at java.base/java.lang.Class.privateGetDeclaredMethods(Class.java:3434)
Then, change build.gradle file's import javax.servlet:javax.servlet-api:4.0.1 alias compileOnly to implementation
dependencies {
...
implementation 'javax.servlet:javax.servlet-api:4.0.1'
...
And successfully get HttpServletRequest's property.
[ShowMethod method1] GET
Thank you for your help in resolving this issue.