I wish to create an agent to attach to our live Tomcat & Weblogic servers which will intercept all method calls to all classes declared in my companies package and do some logging of metrics such as execution time.
I came across the Byte Buddy library which seems to cater for this. However, I am not 100% clear on the approach to creating an agent using Byte Buddy:
byte-buddy-agent
: http://mydailyjava.blogspot.ie/2015/01/make-agents-not-frameworks.html
byte-buddy-agent
so I am
not sure if I am meant to use this.
https://github.com/raphw/byte-buddy/tree/master/byte-buddy-agent
I went with the approach of creating my own agent and packaged it up using Maven to include Byte Buddy as a fat jar (so that the Byte Buddy code is on the class path) which I reference from my catalina.bat
.
Edit: I have since downloaded the source and figured out that the AgentBuilder relies on the byte-buddy-agent package so the above question is irrelevant.
Tomcat starts up fine and I can see that the agent is called as I see the "Entered premain" System.out
.
However I never see the "Intercepted" System.out
when I execute the code on a separate war file deployed to Tomcat.
Edit: Code below updated based on Rafael's response and this is now working.
Can somebody tell me what I might be doing wrong here? I've included the agent code below.
Also, can someone tell me which ElementMatchers
is best for package matching? I tried nameStartsWith
but it had no effect and the API documentation does not state if it is the fully qualified class name.
*Edit: The nameStartsWith does check the package. *
Anyway, thanks in advance for any help!
package com.mycompany.agent;
import java.lang.instrument.Instrumentation;
import java.util.concurrent.Callable;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.SuperCall;
import net.bytebuddy.matcher.ElementMatchers;
public class MyAgent {
public static void premain(String agentArgument, Instrumentation instrumentation) {
System.out.println("Entered premain");
try{
new AgentBuilder.Default()
.withListener( new AgentBuilder.Listener() {
public void onComplete(String arg0) {
System.out.println("Completed - " + arg0);
}
public void onError(String arg0, Throwable arg1) {
System.out.println("Error - " + arg0+", "+arg1.getMessage());
arg1.printStackTrace();
}
public void onIgnored(String arg0) {
System.out.println("Ignored - " + arg0);
}
public void onTransformation(TypeDescription arg0, DynamicType arg1) {
System.out.println("Transformed - " + arg0+", type = "+arg1);
}
})
.rebase(ElementMatchers.nameStartsWith("com.mycompany"))
.transform(new AgentBuilder.Transformer() {
public DynamicType.Builder transform(DynamicType.Builder builder, TypeDescription typeDescription) {
return builder.method(ElementMatchers.any()).intercept(MethodDelegation.to(new Interceptor()));
}
}).installOn(instrumentation);
}
catch (RuntimeException e) {
System.out.println("Exception instrumenting code : "+e);
e.printStackTrace();
}
}
package com.mycompany.agent;
import java.lang.reflect.Method;
import java.util.concurrent.Callable;
import net.bytebuddy.implementation.bind.annotation.AllArguments;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.SuperCall;
@SuppressWarnings("rawtypes")
public class Interceptor {
@RuntimeType
public Object intercept( @SuperCall Callable<?> callable, @AllArguments Object[] allArguments, @Origin Method method, @Origin Class clazz) throws Exception {
long startTime = System.currentTimeMillis();
Object response;
try{
response = callable.call();
}
catch(Exception e) {
System.out.println("Exception occurred in method call: " + methodName(clazz, method, allArguments) + " Exception = " + e);
throw e;
}
finally{
System.out.println("Method " + methodName(clazz, method) + " completed in " + (System.currentTimeMillis() - startTime) + " miliseconds");
}
return response;
}
private String methodName(Class clazz, Method method){
return methodName(clazz, method, null);
}
private String methodName(Class clazz, Method method, Object[] allArguments){
StringBuilder builder = new StringBuilder();
builder.append(clazz.getName());
builder.append(".");
builder.append(method.getName());
builder.append("(");
for(int i = 0; i < method.getParameters().length; i++) {
builder.append(method.getParameters()[i].getName());
if(allArguments != null) {
Object arg = allArguments[i];
builder.append("=");
builder.append(arg != null ? arg.toString() : "null");
}
if(i < method.getParameters().length - 1) {
builder.append(", ");
}
}
builder.append(")");
return builder.toString();
}
Everything seems to be right. You should always try registering an AgentBuider.Listener
which will expose stack traces of unsuccessful instrumentations if Byte Buddy causes an exception for signaling an illegal instrumentation attempt.
I assume that your class's package-private definition of your Interceptor
is the cause of this exception. Your interceptor must be visible to all instrumented code. Otherwise, the class is not invokable.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With