如何在不破坏应用程序输出的情况下重定向JVM输出?

时间:2012-11-18 01:09:53

标签: java python linux jvm

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

-XX:+PrintCompilation
-XX:+PrintGCDetails

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

当我在控制台中将它们打印出来时,我可以获得良好的结果,尽管JVM输出有时会撕掉我的消息,但由于它们处于不同的线程中,因此它是可以理解和可接受的。

当我需要进行一些批量基准测试时,我想 redirect the output into a file pipe (> 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.

9 个答案:

答案 0 :(得分:9)

我建议稍微绕道并考虑使用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

阅读更多内容

答案 1 :(得分:5)

尝试使用System.out.println()代替System.out.print()System.out.println()强制在同步部分内部进行流刷新,并且至少您的输出不会混合。

答案 2 :(得分:5)

使用Log4J或消息驱动的日志框架与System.out.println()

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

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

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

否则,再次需要一个高性能计时器。

答案 3 :(得分:4)

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

为什么?

  1. 这不是'线程安全'。从多个线程记录会导致文本乱码
  2. 它不灵活,因为它是硬编码的,没有配置。
  3. 它不灵活,因为您无法指定您希望在日志中看到的详细程度(例如详细的跟踪/特定调试逻辑/应用警告/应用程序错误处理/应用程序致命错误)。你总是得到很多,需要注释许多代码行,以避免日志过载。
  4. 这是不灵活的,因为你不能指定你或不感兴趣的包/类 - 再次你总是得到很多,并且需要评论许多行,以便更简单和更简单。更具体的
  5. 它不灵活,因为你无法将日志重定向到数据库表&列,文件,电子邮件,消息系统,短信提醒等
  6. 它不灵活,因为您无法将不同的日志级别/包或类流式传输到不同的日志记录目标。此外,您无法将其配置为与应用服务器及其JVM一起登录到同一目的地或不同目的地
  7. 当您将数千/数百万行记录到物理磁盘时速度很慢
  8. 2000年,引入了Log4J 它解决了所有这些问题,从那以后一直是标准的解决方案。虽然有一些最新的&尝试超越Log4J的最佳日志工具,您仍然可以使用Log4J获得强大,灵活的结果。如果您将所有System.out.print调用切换到Log4J,那么您引用的问题和许多其他问题将会消失。

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

答案 4 :(得分:2)

对于-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

答案 5 :(得分:0)

我建议尝试以下方法。这更像是一个黑客,需要一些修修补补。但掌握这种方法可能会带来长期回报。特别是,如果你做了很多基准测试。

我说我很肯定HS(现在,Oracle)应该有一个选项来将编译器输出重定向到一个文件。你只需要很难找到它:-) HS应该有一个选项来打印出他们所有的JVM和编译器选项,其中可能是将输出重定向到文件的选项。

无论如何,我离题了......

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

2)修改System.out以将所有输出重定向到特定的失败,或者只是让它插入一些特殊符号,您可以在其上捕获stdout和stderr。遗憾的是,由于我们公司的政策禁止我们检查src.zip的内容,因此我不能更具体地说明这一特定步骤。我只能想象这一步会有多困难。也许与输出流交换“out”或者修改应用程序直接使用的每种打印方法一样困难。我甚至不知道System.out使用了多少本机

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

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

希望这会有所帮助......

答案 6 :(得分:0)

首先,我会尝试@barracel关于使用System.out.println()的注意事项。

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

答案 7 :(得分:0)

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

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

所以,命令行是这样的:

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

答案 8 :(得分:0)

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

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

这是文件名stdout.txt

中的所有错误以及正常的控制台输出

此外,如果日志包含任何形式的线程/时间信息,您只需使用

即可
sort -n -k 1

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