java代码中动态添加程序执行时间日志

在写java代码的过程中经常会遇到需要打印一个方法的执行时间,如果在代码中直接写开始和结束时间,然后计算运行时间,这样感觉不太好,代码的耦合性太高,可以通过java的动态代理或者cglib 加上annotation(标注)来实现。
1.通过cglib代理打印当前方法的执行时间.
代码如下:

代理类:


import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;

/**
* User: weichun.zhan
* Date: 12-10-25
* Time: 下午3:09
*/
public class ServiceProxy implements MethodInterceptor {

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        Logger logger = LoggerFactory.getLogger(method.getDeclaringClass().getName());
        Object result = null;

        MethodRunTimeLog methodRunTimeLog = method.getAnnotation(MethodRunTimeLog.class);
        if (methodRunTimeLog !=null && methodRunTimeLog.debug()) {
            long start = System.currentTimeMillis();
            result = methodProxy.invokeSuper(o, objects); //因为cglib是通过继承类来实现代理,so需调用invokesuper
            long end = System.currentTimeMillis();
            logger.info("Excute  [{}] method took time [{}]ms.", method.getName(), (end - start));

        } else {
            result = methodProxy.invokeSuper(o, objects);
        }
        return result;
    }

    public Object createProxy(Class targetClass) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(targetClass);
        enhancer.setCallback(new ServiceProxy());
        return enhancer.create();
    }
}
 

调用类:
ServiceProxy test = new ServiceProxy();
MovieInfoServiceServiceImpl proxyTarget = (MovieInfoServiceServiceImpl)test.createProxy(MovieInfoServiceServiceImpl.class);
 


2.当然也可以通过jdk的动态代理来做,代码如下:


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
* User: weichun.zhan
* Date: 12-10-25
* Time: 下午6:03
*/
public class MovieInfoServiceProxy implements InvocationHandler {

    private MovieInfoService impl;

    public MovieInfoServiceProxy(MovieInfoService impl){
        this.impl = impl;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Logger logger = LoggerFactory.getLogger(method.getDeclaringClass().getName());

        Object result = null;
       //获取调用方法的method对象,不能直接调用method,不知为什么还没仔细研究
        MethodRunTimeLog methodRunTimeLog = impl.getClass().getMethod(method.getName(),method.getParameterTypes()).getAnnotation(MethodRunTimeLog.class);
        if (methodRunTimeLog !=null && methodRunTimeLog.debug()) {
            long start = System.currentTimeMillis();
            result = method.invoke(impl, args);
            long end = System.currentTimeMillis();
            logger.info("Excute  [{}] method took time [{}]ms.", method.getName(), (end - start));

        } else {
            result = method.invoke(impl, args);
        }
        return result;
    }
}
 


调用代码:
 MovieInfoService proxyTarget = (MovieInfoService) Proxy.newProxyInstance(MovieInfoService.class.getClassLoader(),
                MovieInfoServiceImpl.class.getInterfaces(),new MovieInfoServiceProxy(new MovieInfoServiceImpl()));
        return proxyTarget;
 

标注类:

import java.lang.annotation.*;

/**
 * User: weichun.zhan
 * Date: 12-10-25
 * Time: 下午4:09
 */

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MethodRunTimeLog {

    boolean debug() default false;

}

 ServiceImpl:

 

@MethodRunTimeLog(debug = true)
    public List<MovieInfo> queryMovieInfos(Collection<String> cids) throws SQLException {
  //具体的实现省略。。。。
}

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
anylog 是一个可以在代码的任意区域无入侵地加入日志的工具,适用于线上问题排查。 anylog 为开发人员提供一个易于使用的平台,帮助开发人员在正在运行的系统随时加入自己想要的日志,而免于修改代码和重启。 使用场景举例     1、一些同学在写代码时,把异常吃掉了,使得问题难以查找,可以使用这个工具,动态打印出被吃掉         的异常,而不用停机。     2、一些项目依赖第三方jar包,如果发生问题,但第三方包日志打印,以往可能需要重新编译第         三方包,加上日志,重启服务,然后排查问题。但使用这个工具,就可以直接动态加入日志,而不用         修改第三方jar包,也不用重启。 已有功能     1、让系统打印某个exception的堆栈,无论此exception是否已经被吃掉都可打印     2、在某个指定类的某个方法的某一行,输出日志。     3、在某个指定类的某个方法的开始,输出日志。     4、在某个指定类的某个方法的结束,输出日志。       5、打印方法耗时,支持方法嵌套。     如果需要扩展新的功能(例如输出jvm的cpu占用,内存大小等),只需要实现spi的     com.github.jobop.anylog.spi.TransformDescriptor      和com.github.jobop.anylog.spi.TransformHandler接口,     然后把实现的jar包放到providers目录即可识别。 使用方法     1、获取运行程序:         1)可以到以下地址获取正式发行版:https://github.com/jobop/release/tree/master/anylog         2)你也可以clone下源码后,执行如下命令,生成运行程序,生成的运行程序将在dist目录下             生成windows版本:  mvn install             生成linux版本:  mvn install -Plinux     2、直接执行startup.bat或者startup.sh即可运行起来     3、访问 http://127.0.0.1:52808 即可使用 功能扩展     anylog利用spi机制实现其扩展,如果你想要对anylog增加新的功能(例如添加返回值打印的功能)可以按照如下步骤操作:     1、使用如下命令,生成一个spi实现工程,并导入eclipse     mvn archetype:generate -DarchetypeGroupId=com.github.jobop -DarchetypeArtifactId=anylogspi-archetype -DarchetypeVersion=1.0.4     2、参照该工程已有的两个例子(一个是在方法开始插入日志,一个是在方法结束插入日志),实现TransformDescriptor和TransformHandler接口     3、把两个接口实现类的全路径,分别加到以下两个文件         src/main/resources/META-INF/services/com.github.jobop.anylog.spi.TransformDescriptor         src/main/resources/META-INF/services/com.github.jobop.anylog.spi.TransformHandler     4、执行mvn install打包,在dist下会生成你的扩展实现jar。     5、把扩展实现jar拷贝到anylog的providers目录下,重启即可生效。     tips:在实现spi时,我们提供了SpiDesc注解,该注解作用在你实现的TransformDescriptor上,可以用来生成功能描述文字。          如果要深入了解spi机制,请自行google:java spi 标签:anylog
### 回答1: hook 是一种通过在程序运行期间动态地将自己的代码插入到其他程序来实现某些功能的技术。要在 Java 代码使用 hook,需要使用 Java 的反射功能来动态地修改类的方法。 下面是一个简单的例子,展示了如何使用 Java 的反射功能动态地插入代码: ``` import java.lang.reflect.Method; public class HookExample { public static void main(String[] args) throws Exception { // 获取要插入代码的目标方法 Method targetMethod = TargetClass.class.getMethod("targetMethod", int.class, String.class); // 使用 Java 的反射功能动态地修改方法 HookUtils.hook(targetMethod, new MethodReplacement() { @Override public Object replace(Object target, Object[] args) throws Throwable { // 在这里插入自己的代码 System.out.println("Hook successfully!"); return null; } }); // 调用目标方法 TargetClass target = new TargetClass(); target.targetMethod(1, "hello"); } } ``` 在这个例子,我们使用了 Java 的反射功能来获取要插入代码的目标方法 `targetMethod`,并使用 `HookUtils.hook` 方法动态地修改这个方法。在 `replace` 方法,我们可以插入自己的代码。最后,我们调用了目标方法 `targetMethod`,从而触发了我们插入的代码。 ### 回答2: 通过hook技术,在Java代码插入自己的代码,可以借助于Java Agent机制来实现。Java Agent是Java虚拟机提供的一种机制,允许在虚拟机加载类之前或之后对类进行修改或增强。 具体实现步骤如下: 1. 创建一个代理类,用于对原始类进行修改或增强。代理类需要实现一个接口,这个接口包含了原始类的所有方法。在代理类,可以在方法执行前后插入自己的代码逻辑。 2. 创建一个Java Agent,并在其使用Instrumentation API来做字节码转换。在Agent的premain方法,使用Instrumentation对象的方法进行字节码转换。将原始类转换为代理类,然后在转换过程,将自己的代码插入到要转换的方法。 3. 在代理类,可以在方法执行前后注入自己的代码。例如,在方法执行前输出一段日志,在方法执行后做一些额外的操作。 4. 将Agent打包成独立的jar文件,并在Java启动参数增加启动Agent的参数。在启动Java程序时,使用-javaagent参数引入Agent。这样就可以在Java程序运行时自动加载Agent,并在加载类之前进行字节码转换。 通过上述步骤,就可以使用hook技术将自己的代码插入到Java代码,实现代码的修改或增强。这种方式可以用于实现一些动态代理、AOP切面编程等功能。需要注意的是,使用hook技术需要对Java字节码有一定的了解,并且要小心不要对原始类的功能造成破坏或影响。 ### 回答3: 在Java,可以通过使用Hook技术将自己的代码插入到已有的Java代码。Hook是一种钩子函数,可以在程序运行时动态地修改代码逻辑。 要实现这个目标,可以使用Java的反射机制来实现,反射可以在运行时获取类、方法和字段的信息,通过反射可以动态地调用类的方法。 首先,需要找到目标代码适合插入的位置。可以在目标方法的开始或结束处插入代码。假设有一个目标方法public void targetMethod(),我们希望在其开始处插入代码。 然后,定义一个新的类,包含你想要插入的代码逻辑。这个类可以继承自java.lang.instrument.ClassFileTransformer接口,实现其的transform方法。在transform方法,可以获取传入的类和类文件字节码,然后将你的代码插入到其。 接下来,在你的代码使用Instrumentation的addTransformer方法注册刚刚创建的类,例如:instrumentation.addTransformer(new MyTransformer())。 最后,在目标Java代码的入口处调用Instrumentation的retransformClasses方法,重新转换目标类。例如:instrumentation.retransformClasses(TargetClass.class)。 通过上述步骤,你的代码就会被插入到目标Java代码。你可以在MyTransformer类的transform方法编写你自己的代码,这段代码会在目标方法执行之前被调用。 需要注意的是,使用Hook技术需要对Java的Instrumentation API有一定的了解,同时需要在运行时添加javaagent参数来指定你的Agent类。此外,需要注意插入代码的位置和插入逻辑的正确性,确保你的代码对目标方法不会产生负面影响。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值