使用 byte buddy 或一些库进行分析
Profiling using byte buddy or some library
我正在尝试构建一个查询日志记录分析器,它可以计算每个查询的执行时间,并在查询花费更多时间时进行记录。
与 Wrapper 相比,使用 AspectJ 需要更多时间。因此,如果有性能改进的余地,我想使用 byte buddy 或其他一些库。
这是我目前使用 AspectJ 的实现。
@Aspect
public class QueryLoggerProfiler {
private static final Logger LOGGER = Logger.getLogger(QueryLoggerProfiler.class.getName());
public static final String QUERY_LOGGING_POINTCUT = "execution(* com.abc.PreparedStatement.execute*(..))";
@
Pointcut(QUERY_LOGGING_POINTCUT)
private void queryPointcut() {}
@
Around("queryPointcut()")
public Object profile(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
long start = System.currentTimeMillis();
Object output = joinPoint.proceed();
long elapsedTime = System.currentTimeMillis() - start;
if (elapsedTime >= 5) {
LOGGER.info(">>>>>>>>>>>>>>>>>>>... Going to call the method ... " + method.getName());
LOGGER.info(">>>>>>>>>>>>>>>>>>>... With parameter ... " + method.getParameters());
LOGGER.info(">>>>>>>>>>>>>>>>>>>... Method execution time: " + elapsedTime + " milliseconds.");
}
return output;
}
}
有没有什么方法既可以登录又没有性能瓶颈?
最简单的方法是将 Advice
组件与 AgentBuilder
一起使用。这将允许您定义类似于以下内容的代理:
public static void premain(String arg, Instrumentation inst) {
new AgentBuilder.Default()
.type(named("com.abc.PreparedStatement"))
.transform(new Transformer() {
public DynamicType.Builder transform(DynamicType.Builder builder) {
return builder.visit(Advice.to(MyAdvice.class).on(nameStartsWith("execute")));
}
}).installOn(inst);
}
这将为指定的类型和名称应用 MyAdvice
class 中定义的方法。查看 Advice
的 javadoc 以了解如何获取参数或如何指定要调用的方法。
有关如何使用 Byte Buddy 和 Javaagents 检测代码的一般信息,请参阅 this article。
实际上我的代码如下。
public static void premain(String agentArgument, Instrumentation instrumentation) {
System.out.println(">>>>>>>>>>>>>>>>>> Entered premain");
try {
new AgentBuilder.Default()
.type(ElementMatchers.nameStartsWith("com.mycomp.hikari.LoggingPreparedStatement"))
.transform((builder, typeDescription, classLoader) -> builder
.method(ElementMatchers.any())
.intercept(MethodDelegation.to(new Interceptor())))
.installOn(instrumentation);
} catch (RuntimeException e) {
System.out.println(">>>>>>>>>>>>>>>>>> Exception instrumenting code : " + e);
e.printStackTrace();
}
}
然后 class 拦截器作为....
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 {
long elapsedTime = System.currentTimeMillis() - startTime;
if (elapsedTime > 3)
System.out.println(">>>>>>>>>>>>>>>>>>>> .... Method " + methodName(clazz, method, allArguments) + " completed in " + elapsedTime + " milliseconds");
}
return response;
}
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();
}
}
我正在尝试构建一个查询日志记录分析器,它可以计算每个查询的执行时间,并在查询花费更多时间时进行记录。 与 Wrapper 相比,使用 AspectJ 需要更多时间。因此,如果有性能改进的余地,我想使用 byte buddy 或其他一些库。
这是我目前使用 AspectJ 的实现。
@Aspect
public class QueryLoggerProfiler {
private static final Logger LOGGER = Logger.getLogger(QueryLoggerProfiler.class.getName());
public static final String QUERY_LOGGING_POINTCUT = "execution(* com.abc.PreparedStatement.execute*(..))";
@
Pointcut(QUERY_LOGGING_POINTCUT)
private void queryPointcut() {}
@
Around("queryPointcut()")
public Object profile(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
long start = System.currentTimeMillis();
Object output = joinPoint.proceed();
long elapsedTime = System.currentTimeMillis() - start;
if (elapsedTime >= 5) {
LOGGER.info(">>>>>>>>>>>>>>>>>>>... Going to call the method ... " + method.getName());
LOGGER.info(">>>>>>>>>>>>>>>>>>>... With parameter ... " + method.getParameters());
LOGGER.info(">>>>>>>>>>>>>>>>>>>... Method execution time: " + elapsedTime + " milliseconds.");
}
return output;
}
}
有没有什么方法既可以登录又没有性能瓶颈?
最简单的方法是将 Advice
组件与 AgentBuilder
一起使用。这将允许您定义类似于以下内容的代理:
public static void premain(String arg, Instrumentation inst) {
new AgentBuilder.Default()
.type(named("com.abc.PreparedStatement"))
.transform(new Transformer() {
public DynamicType.Builder transform(DynamicType.Builder builder) {
return builder.visit(Advice.to(MyAdvice.class).on(nameStartsWith("execute")));
}
}).installOn(inst);
}
这将为指定的类型和名称应用 MyAdvice
class 中定义的方法。查看 Advice
的 javadoc 以了解如何获取参数或如何指定要调用的方法。
有关如何使用 Byte Buddy 和 Javaagents 检测代码的一般信息,请参阅 this article。
实际上我的代码如下。
public static void premain(String agentArgument, Instrumentation instrumentation) {
System.out.println(">>>>>>>>>>>>>>>>>> Entered premain");
try {
new AgentBuilder.Default()
.type(ElementMatchers.nameStartsWith("com.mycomp.hikari.LoggingPreparedStatement"))
.transform((builder, typeDescription, classLoader) -> builder
.method(ElementMatchers.any())
.intercept(MethodDelegation.to(new Interceptor())))
.installOn(instrumentation);
} catch (RuntimeException e) {
System.out.println(">>>>>>>>>>>>>>>>>> Exception instrumenting code : " + e);
e.printStackTrace();
}
}
然后 class 拦截器作为....
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 {
long elapsedTime = System.currentTimeMillis() - startTime;
if (elapsedTime > 3)
System.out.println(">>>>>>>>>>>>>>>>>>>> .... Method " + methodName(clazz, method, allArguments) + " completed in " + elapsedTime + " milliseconds");
}
return response;
}
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();
}
}