【发布时间】:2009-06-02 14:51:43
【问题描述】:
我正在寻找一种简单的方法(例如,单个脚本/批处理文件或简单程序)来检测一组 Java 源文件,以便每个方法在进入和退出时都有一个添加的 System.out.println 语句。
我找不到这样的实用程序。我需要这个的原因(以及为什么它必须在源文件而不是 .class 文件上工作)是目标系统是嵌入式 Java 系统..
提前致谢, pd
【问题讨论】:
我正在寻找一种简单的方法(例如,单个脚本/批处理文件或简单程序)来检测一组 Java 源文件,以便每个方法在进入和退出时都有一个添加的 System.out.println 语句。
我找不到这样的实用程序。我需要这个的原因(以及为什么它必须在源文件而不是 .class 文件上工作)是目标系统是嵌入式 Java 系统..
提前致谢, pd
【问题讨论】:
这是Aspect Oriented Programming 的绝佳用例。
【讨论】:
您可能没有找到太多信息,因为使用 System.out 是一种非常可怕的检测应用程序的方式。
您想做什么 - 分析性能、代码覆盖率等?如果是这样,那么两者都有很多工具。
【讨论】:
我以前在没有其他方法的情况下这样做过。实际上,一旦我找到正确的方法,我就每隔几行插入一个 sysout。
(例如,我刚刚在一个 JVM 上工作,其中引发异常的 RMI 调用被静默吃掉,调用线程无限期挂起!哦,当然,我们不能使用调试器。你怎么办找到那个??)
无论如何,只运行一个在任何地方插入调试输出的程序都无济于事。你会迷失分析输出的页面。而是尝试将问题所在的位置括起来并在此处手动插入输出行。
如果您的线程“消失”在单行上或出现意外转折,请将该行分解为最少的可运行部分,找出哪部分失败,然后深入研究并重复 - 没什么可做的。
顺便说一句,另一个有用的工具——即使在非常旧的 JVM 上,你也可以在任何时候抛出“(new Exception()).printStackTrace();”在不影响程序流程的情况下转储堆栈。如果您发现自己身处某个地点但不知道自己是如何到达那里的,这会有所帮助。
最后,如果你真的很懒,你可以使用一个静态方法来完成你所有的调试输出(一个只委托给 System.out.println 的),并添加几行来获取堆栈跟踪,抓住只有“调用”行/方法并在调试语句之前将其打印出来,为您提供穷人的日志记录实用程序。
只是我从多年在嵌入式平台上使用旧版本 java 中学到的一些手动调试技巧。
【讨论】:
我同意 Kees 的观点,您应该使用面向方面的编程来自动为每个函数添加日志记录。我使用以下代码对计时做了类似的事情:
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.util.StopWatch;
@Aspect
public class ProfilingAspect {
@Around("methodsToBeProfiled()")
public Object profile(ProceedingJoinPoint pjp) throws Throwable {
StopWatch sw = new StopWatch(pjp.getSignature().getClass().getSimpleName());
try {
sw.start(pjp.getSignature().getName());
return pjp.proceed();
} finally {
sw.stop();
LogFactory.getLog(pjp.getSignature().getClass()).debug(sw.shortSummary());
}
}
@Pointcut("execution(public * *.*(..))")
public void methodsToBeProfiled(){}
}
这几乎是您可以添加的最简单的方面。关键部分是切入点注释,它声明您希望将此方面应用于每个包中每个公共类的每个公共方法,无论其返回类型或输入参数是什么。你可能想让它更具体。 @Around 注释指的是切入点,并指定了一堆你想“围绕”函数执行的代码。在您的情况下,如果您只想记录一个被调用的函数,您可以根据您想要记录的位置将 @Around 替换为 @Before 或 @AfterReturning 。如果您正在构建应用程序的所有内容,请针对“编译时编织”而不是加载时间。它降低了应用程序的运行时要求。
【讨论】:
我为 slf4j 编写了一个扩展,它提供了您正在寻找的功能(除了作为记录器调用而不是 System.out,但将记录器调用发送到 System.out 很简单)。
【讨论】: