Использование Byte Buddy для агента Java
Я хочу создать агента, который будет подключаться к нашим работающим серверам Tomcat и Weblogic, который будет перехватывать все вызовы методов для всех классов, объявленных в пакете моей компании, и выполнять некоторую регистрацию метрик, таких как время выполнения.
Я наткнулся на библиотеку Byte Buddy, которая, кажется, обслуживает это. Тем не менее, я не на 100% уверен в подходе к созданию агента с использованием Byte Buddy:
- Следующая статья предполагает, что каждый создает собственного агента и не упоминает о
byte-buddy-agent
: http://mydailyjava.blogspot.ie/2015/01/make-agents-not-frameworks.html - Тем не менее, я вижу, что кто-то создал свой собственный
byte-buddy-agent
поэтому я не уверен, если я собираюсь использовать это. https://github.com/raphw/byte-buddy/tree/master/byte-buddy-agent
Я пошел с подходом создания своего собственного агента и упаковал его с помощью Maven, чтобы включить Byte Buddy в качестве толстой банки (так, чтобы код Byte Buddy был на пути к классам), на который я ссылаюсь из своего catalina.bat
,
Изменить: с тех пор я скачал исходный код и выяснил, что AgentBuilder полагается на пакет byte-buddy-agent, поэтому вышеупомянутый вопрос не имеет значения.
Tomcat запускается нормально, и я вижу, что агент вызывается, когда я вижу "Введенный premain" System.out
,
Однако я никогда не вижу "Перехваченный" System.out
когда я выполняю код в отдельном файле войны, развернутом в Tomcat. Изменить: Код ниже обновляется на основе ответа Рафаэля, и теперь это работает.
Может кто-нибудь сказать мне, что я могу делать здесь не так? Я включил код агента ниже.
Кроме того, кто-то может сказать мне, какие ElementMatchers
лучше всего подходит для соответствия пакетов? Я старался nameStartsWith
но это не имело никакого эффекта, и документация API не указывает, является ли это полностью определенным именем класса.
* Редактировать: nameStartsWith проверяет пакет. *
В любом случае, заранее спасибо за любую помощь!
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();
}
1 ответ
Кажется, все правильно. Вы должны всегда пытаться зарегистрировать AgentBuider.Listener
который будет отображать следы неудачных контрольно-измерительных приборов, если Byte Buddy вызывает исключение для сигнализации о недопустимой попытке контрольно-измерительных приборов.
Я предполагаю, что пакетно-личное определение вашего класса вашего Interceptor
является причиной этого исключения. Ваш перехватчик должен быть виден всему инструментальному коду. В противном случае класс не может быть вызван.