【问题标题】:Instrument java source files with System.out.println statements?使用 System.out.println 语句检测 java 源文件?
【发布时间】:2009-06-02 14:51:43
【问题描述】:

我正在寻找一种简单的方法(例如,单个脚本/批处理文件或简单程序)来检测一组 Java 源文件,以便每个方法在进入和退出时都有一个添加的 System.out.println 语句。

我找不到这样的实用程序。我需要这个的原因(以及为什么它必须在源文件而不是 .class 文件上工作)是目标系统是嵌入式 Java 系统..

提前致谢, pd

【问题讨论】:

    标签: java debugging


    【解决方案1】:

    这是Aspect Oriented Programming 的绝佳用例。

    【讨论】:

    • .. 与日志记录 (log4j) 一起使用,而不是使用 'System.out.println'
    【解决方案2】:

    您可能没有找到太多信息,因为使用 System.out 是一种非常可怕的检测应用程序的方式。

    您想做什么 - 分析性能、代码覆盖率等?如果是这样,那么两者都有很多工具。

    【讨论】:

    • 我正在尝试对一个棘手的错误进行第一阶段分析 - 首先获取覆盖信息,然后在相关位置添加更多打印输出以查看发生了什么。我应该提到我目前不能使用调试器来单步执行代码(系统的调试支持暂时被破坏),因此必须依赖打印输出。我很清楚这是一种可怕的方法,但我还是想尝试一下。
    • 在这种情况下,也许您可​​以使用某种 AOP 在每个方法调用中交织通知以在之前/之后打印语句?不确定这是否是嵌入式设备的选项。
    • 仍然应该查看 log4j 而不是 System.out.println()。不会帮助您自动插入,但一旦出现这些行,您将获得更多控制权。
    【解决方案3】:

    我以前在没有其他方法的情况下这样做过。实际上,一旦我找到正确的方法,我就每隔几行插入一个 sysout。

    (例如,我刚刚在一个 JVM 上工作,其中引发异常的 RMI 调用被静默吃掉,调用线程无限期挂起!哦,当然,我们不能使用调试器。你怎么办找到那个??)

    无论如何,只运行一个在任何地方插入调试输出的程序都无济于事。你会迷失分析输出的页面。而是尝试将问题所在的位置括起来并在此处手动插入输出行。

    如果您的线程“消失”在单行上或出现意外转折,请将该行分解为最少的可运行部分,找出哪部分失败,然后深入研究并重复 - 没什么可做的。

    顺便说一句,另一个有用的工具——即使在非常旧的 JVM 上,你也可以在任何时候抛出“(new Exception()).printStackTrace();”在不影响程序流程的情况下转储堆栈。如果您发现自己身处某个地点但不知道自己是如何到达那里的,这会有所帮助。

    最后,如果你真的很懒,你可以使用一个静态方法来完成你所有的调试输出(一个只委托给 System.out.println 的),并添加几行来获取堆栈跟踪,抓住只有“调用”行/方法并在调试语句之前将其打印出来,为您提供穷人的日志记录实用程序。

    只是我从多年在嵌入式平台上使用旧版本 java 中学到的一些手动调试技巧。

    【讨论】:

      【解决方案4】:

      我同意 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 。如果您正在构建应用程序的所有内容,请针对“编译时编织”而不是加载时间。它降低了应用程序的运行时要求。

      【讨论】:

        【解决方案5】:

        我为 slf4j 编写了一个扩展,它提供了您正在寻找的功能(除了作为记录器调用而不是 System.out,但将记录器调用发送到 System.out 很简单)。

        看看http://www.slf4j.org/extensions.html#javaagent

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2020-03-19
          • 2013-09-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多