【发布时间】:2012-01-25 23:22:26
【问题描述】:
我听说使用System.out.println 进行日志记录是一种非常糟糕的做法,这可能会导致服务器出现故障。
我不使用这种方法,但我很想知道为什么 System.out.println 在后端代码中使用时会产生如此垃圾的东西。
【问题讨论】:
-
我相信这个问题(或背后的理由)比答案更有趣。
我听说使用System.out.println 进行日志记录是一种非常糟糕的做法,这可能会导致服务器出现故障。
我不使用这种方法,但我很想知道为什么 System.out.println 在后端代码中使用时会产生如此垃圾的东西。
【问题讨论】:
System.out.println 是一个 IO 操作,因此非常耗时。 在您的代码中使用它的问题是,您的程序将等到 println 完成。对于小型网站,这可能不是问题,但一旦加载或多次迭代,您就会感到痛苦。
更好的方法是使用日志框架。 他们使用消息队列并仅在没有其他输出时才写入。
另一个好处是您可以为不同的目的配置单独的日志文件。 您的 Ops 团队一定会喜欢您。
在此处阅读更多信息:
【讨论】:
这是一种不好的做法,因为当您的应用程序投入生产时,您无法将应用程序日志与服务器日志分开。
产品团队希望您将应用程序生成的日志与来自应用程序服务器(tomcat、websphere 等)的日志分开:他们希望能够以不同于应用程序本身的方式监控应用程序服务器。
此外,使用 System.out,您无法定义日志级别:在生产环境中,您不想打印调试信息。
【讨论】:
这被认为是坏的,因为System.out.println(); 吃更多的 cpu,因此输出缓慢意味着损害性能。 (实际上每个 I/O 操作都吃 cpu)。
【讨论】:
System.out.println 是 blocking - 就像 Java 中的大多数 I/O 调用(以及大多数其他环境)。这会完全扼杀吞吐量。
原因不是服务器可能出现故障,而是可能很难在服务器上找到这样的输出。您应该始终使用某种具有已定义行为和输出文件的日志记录框架。
【讨论】:
一方面,有多个请求访问您的服务器并在System.out 上打印日志并不是一个好习惯。
System.out 未同步。必须有一个并发管理来通过System.out管理打印
System.out 确定日志级别。您不能即时将日志级别分隔为单独的输出。我希望这会有所帮助。
【讨论】:
a. 格式化,限制记录器可实现的日志内容
b. 多目标日志记录——文件、控制台、数据库【讨论】:
使用标准输出是不好的做法。但是,如果您有一个库或使用 System.out 和 System.err 的代码,您可以编写自己的 PrintStream 来记录线程名称和 info() 和 error() 文本。完成此操作后,您可能会更轻松地使用 System.out,因为它将写入日志,例如日志4j。
理想情况下,您将直接使用正确的日志,尤其是调试级别的日志记录。恕我直言,只要您不使用内置的 System.out/err,它就没有那么重要了! (诚然,一个很大的假设)
无论你使用 System.out 重定向到文件还是使用 log4j 或 Java Logger 写入文件,性能几乎完全相同。
【讨论】:
还有一个原因是 System.out 和 err 是 PrintStreams,它们正在消耗所有底层 IOExceptions。请参阅 PrintStreams 的这种方法:
/**
* Writes the specified byte to this stream. If the byte is a newline and
* automatic flushing is enabled then the <code>flush</code> method will be
* invoked.
*
* <p> Note that the byte is written as given; to write a character that
* will be translated according to the platform's default character
* encoding, use the <code>print(char)</code> or <code>println(char)</code>
* methods.
*
* @param b The byte to be written
* @see #print(char)
* @see #println(char)
*/
public void write(int b) {
try {
synchronized (this) {
ensureOpen();
out.write(b);
if ((b == '\n') && autoFlush)
out.flush();
}
}
catch (InterruptedIOException x) {
Thread.currentThread().interrupt();
}
catch (IOException x) {
trouble = true;
}
}
/**
* Flushes the stream and checks its error state. The internal error state
* is set to <code>true</code> when the underlying output stream throws an
* <code>IOException</code> other than <code>InterruptedIOException</code>,
* and when the <code>setError</code> method is invoked. If an operation
* on the underlying output stream throws an
* <code>InterruptedIOException</code>, then the <code>PrintStream</code>
* converts the exception back into an interrupt by doing:
* <pre>
* Thread.currentThread().interrupt();
* </pre>
* or the equivalent.
*
* @return <code>true</code> if and only if this stream has encountered an
* <code>IOException</code> other than
* <code>InterruptedIOException</code>, or the
* <code>setError</code> method has been invoked
*/
public boolean checkError() {
if (out != null)
flush();
if (out instanceof java.io.PrintStream) {
PrintStream ps = (PrintStream) out;
return ps.checkError();
}
return trouble;
}
因此,来自底层流的 IOException 总是被消耗掉,而且人们通常不会在 System out 上调用 checkError,所以他们甚至不知道发生了什么事。
【讨论】: