【问题标题】:How to redirect JVM output without tear up output from the application?如何在不破坏应用程序输出的情况下重定向 JVM 输出?
【发布时间】:2012-11-18 01:09:52
【问题描述】:

最近我正在编写一些微基准代码,所以我必须打印出 JVM 行为以及我的基准信息。我用

-XX:+PrintCompilation
-XX:+PrintGCDetails

和其他获取 JVM 状态的选项。对于基准信息,我只是使用System.out.print() 方法。因为我需要知道我打印的消息的顺序和JVM的输出。

我只是在控制台打印出来就可以得到很好的结果,虽然JVM输出有时会撕掉我的消息,但是由于它们在不同的线程中,这是可以理解和接受的。

当我需要做一些批处理基准测试时,我想redirect the output into a filepipe (> in Linux system),并使用 python 从文件中获取结果并分析一下。

问题来了:

The JVM output always overlapped with the messages I printed in the Java application.它破坏了消息的完成。

知道如何处理这种情况吗?我需要both the JVM output and application output in the same place in order to preserve the sequence because it is important. And they do not overlap on each other so I don't lose anything.

【问题讨论】:

    标签: java python linux jvm


    【解决方案1】:

    我建议绕道而行,看看使用 Java Instrumentation API - 使用(编写)一个简单的 Java 代理 来执行此操作。从您的基准测试的角度来看,这也将为您提供更多的权力。您可以使用 Java 代理记录所有内容(因此不同的记录器线程之间不会发生争用)。

    您可以在http://www.javabeat.net/2012/06/introduction-to-java-agents/http://today.java.net/pub/a/today/2008/04/24/add-logging-at-class-load-time-with-instrumentation.html 阅读更多内容

    【讨论】:

    • 好主意,我尝试重定向代理中的stdout和stderr,我可以重定向我的java代码的输出,但JVM输出没有重定向。还有你提到的文章中介绍的方法——加载类时修改字节码,我得知道JVM在输出信息时调用了什么,然后也许我可以修改它,但我没有那种信息,您有什么建议吗?
    【解决方案2】:

    尝试使用System.out.println() 而不是System.out.print()System.out.println() 强制在同步部分内刷新流,至少您的输出不会那么混杂。

    【讨论】:

      【解决方案3】:

      使用 Log4J 或消息驱动的日志框架,而不是 System.out.println()

      Log4J 使用消息事件模型来保证消息的顺序。 此外,各种“附加程序”可用于记录到数据库或其他输出/文件,允许通过 Java 包和其他属性进行分离,从而不会混合数据。

      此外,按照这些思路,请考虑使用高性能计时器和/或不要尝试测量非常短(毫秒)的事件。原因是对System.currentTimeMillis() 的调用将反过来调用操作系统时钟。在每个操作系统上,都会有一些“时钟漂移”和缓存,这样底层系统函数可能会返回相同的值,从而导致实际时间出现 +/- 30 毫秒的偏移。为了解决这个问题或提高准确性,请将被测量的函数分组为足够大的样本大小,然后除以迭代次数。

      例如,执行 10K 次平均 1-2 毫秒的操作作为一个测量操作。然后除以 10K 得到每次操作的时间。

      否则,同样需要高性能计时器。

      【讨论】:

      • 这没有回答原始问题。 Log4J 对 HotSpot 诊断日志没有帮助,也没有帮助解决将它们交错在与应用程序输出相同的文件中以反映它们的时间关系的问题。
      • 问题中没有提到 HotSpot 诊断。问题是如何在不重叠的情况下保留日志记录顺序,而解决方案当然是一种方法。
      • 这个问题(当我阅读它时,它已被编辑)在第一段中提到了 HotSpot 诊断,如 -XX:+PrintCompilation,以及需要与其他日志记录一起正确排序。跨度>
      【解决方案4】:

      通过 System.out.print/println 直接记录被认为是不好的做法。

      为什么?

      1. 它不是“线程安全的”。从多个线程记录会导致文本乱码
      2. 它不灵活,因为它是硬编码的并且没有配置。
      3. 这是不灵活的,因为您无法指定希望在日志中看到的详细程度(例如详细的跟踪/特定的调试逻辑/应用程序警告/应用程序错误处理/应用程序致命错误)。你总是得到很多,需要注释很多行代码以避免日志过载。
      4. 这是不灵活的,因为您无法指定您对哪些包/类感兴趣或不感兴趣 - 同样,您总是会得到很多,并且需要注释很多行以获得更简单和更具体的内容
      5. 不灵活,因为您无法将日志重定向到数据库表和列、文件、电子邮件、消息系统、SMS 警报等
      6. 这是不灵活的,因为您不能将不同的日志级别/包或类流式传输到不同的日志记录目标。此外,您不能将其配置为与应用服务器登录到相同的目的地或不同的目的地,并且它是 JVM
      7. 当您将数千/数百万行记录到物理磁盘时会很慢

      2000 年,Log4J 被引入。
      它解决了所有这些问题,从那时起就或多或少成为标准解决方案。虽然有一些最新最好的日志工具试图超越 Log4J,但您仍然可以使用 Log4J 获得强大、灵活的结果。如果您将所有 System.out.print 调用切换到 Log4J,那么您引用的问题和许多其他问题都会消失。

      http://logging.apache.org/log4j/1.2/manual.html

      【讨论】:

        【解决方案5】:

        对于-XX:+PrintCompilation,您可以改用-XX:+UnlockDiagnosticVMOptions -XX:+LogCompilation 标志在单独的“hotspot.log”文件中获取“详细”输出。此文件为 XML 格式,包含来自-XX:+PrintCompilation 的信息以及此类编译的原因。可以通过-XX:LogFile=<new_hotspot_log> 更改文件路径。参考:https://wiki.openjdk.java.net/display/HotSpot/LogCompilation+overview

        对于-XX:+PrintGCDetails,您可以使用-Xloggc:<gc_log> 将GC 输出重定向到指定文件。参考:java -X

        【讨论】:

          【解决方案6】:

          我建议尝试以下方法。这更像是一种黑客行为,需要一些修补。但从长远来看,掌握这种方法可能会有所回报。特别是,如果您进行大量基准测试。

          话虽如此,我很肯定 HS(现在是 Oracle)应该可以选择将编译器输出重定向到文件。您只需要足够努力地寻找它:-) HS 应该有一个选项来打印出所有 JVM 和编译器选项,其中可能是将输出重定向到文件的选项。

          不管怎样,我跑题了……

          1) 您的 $JAVA_HOME 或 %JAVA_HOME% 中应该有 src.zip。它包含 Java 类库的源代码。

          2) 修改 System.out 以将所有输出重定向到特定的失败,或者简单地使其插入一些特殊符号,您可以在该符号上使用 grep 来捕获 stdout 和 stderr。不幸的是,我不能对这个特定步骤更具体,因为我们公司的政策禁止我们检查 src.zip 的内容。我只能想象这一步会有多困难。也许它就像用你的输出流交换“out”一样微不足道,或者像修改你的应用程序直接使用的每一个打印方法一样困难。我什至不知道 System.out 使用了多少本地人

          3) 将编译后的版本放入 jar 文件中。

          4) 将此选项添加到您的命令行: -Xbootclasspath/p:full_path_to_your_jar 这将告诉 JVM 首先使用您的类版本。 “P”代表前置。

          希望这会有所帮助...

          【讨论】:

            【解决方案7】:

            首先,我会尝试@barracel 关于使用 System.out.println() 的说明。

            我对 Java 了解不多,但您也可以将所有调试消息写到 stderr 并将 stdout 留给 JVM。这可以防止当多个线程写入同一个文件描述符时明显发生的标准输出污染。

            【讨论】:

              【解决方案8】:

              尝试拆分 JVM 和您的应用程序的输出。

              • 将 JVM 的信息输出到标准输出
              • 使用“System.err.println()”将应用程序的信息输出到 stderr
              • 使用您喜欢的工具分析输出。

              所以,命令行是这样的:

              $java -XX:+PrintCompilation -XX:+PrintGCDetails MainClass 1>stdout.txt 2>stderr.txt
              

              【讨论】:

                【解决方案9】:

                为了以非重叠方式输出,请使用 System.out.println。然后你可以像这样重定向到同一个文件:

                java -XX:+PrintCompilation -XX:+PrintGCDetails MainClass 1>stdout.txt 2>&1
                

                这是在文件名 stdout.txt 中包含所有错误以及正常控制台输出

                另外,如果日志有任何形式的线程/时间信息,你可以简单地使用

                sort -n -k 1
                

                其中 -k 1 代表您拥有线程/数据(纪元)信息的列。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-11-14
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-09-11
                  • 2010-11-05
                  • 1970-01-01
                  相关资源
                  最近更新 更多