【问题标题】:Do not use System.out.println in server side code不要在服务器端代码中使用 System.out.println
【发布时间】:2012-01-25 23:22:26
【问题描述】:

我听说使用System.out.println 进行日志记录是一种非常糟糕的做法,这可能会导致服务器出现故障。

我不使用这种方法,但我很想知道为什么 System.out.println 在后端代码中使用时会产生如此垃圾的东西。

【问题讨论】:

  • 我相信这个问题(或背后的理由)比答案更有趣。

标签: java logging backend


【解决方案1】:

System.out.println 是一个 IO 操作,因此非常耗时。 在您的代码中使用它的问题是,您的程序将等到 println 完成。对于小型网站,这可能不是问题,但一旦加载或多次迭代,您就会感到痛苦。

更好的方法是使用日志框架。 他们使用消息队列并仅在没有其他输出时才写入。

另一个好处是您可以为不同的目的配置单独的日志文件。 您的 Ops 团队一定会喜欢您。

在此处阅读更多信息:

【讨论】:

    【解决方案2】:

    在 Java 杂志 11 月/12 月版中查看 Adam Biens article,了解 JEE6 应用程序的压力测试 - 它是免费的 online,您只需订阅即可。

    他在第 43 页显示,当插入一个带有修复字符串的 System.out.println 时,每秒设法处理 1700 个事务的服务器应用程序下降到只有 800 个。

    【讨论】:

      【解决方案3】:

      这是一种不好的做法,因为当您的应用程序投入生产时,您无法将应用程序日志与服务器日志分开。

      产品团队希望您将应用程序生成的日志与来自应用程序服务器(tomcat、websphere 等)的日志分开:他们希望能够以不同于应用程序本身的方式监控应用程序服务器。

      此外,使用 System.out,您无法定义日志级别:在生产环境中,您不想打印调试信息。

      【讨论】:

        【解决方案4】:

        这被认为是坏的,因为System.out.println(); 吃更多的 cpu,因此输出缓慢意味着损害性能。 (实际上每个 I/O 操作都吃 cpu)。

        【讨论】:

        • 通常主要问题不是 CPU 使用率,而是 I/O 使用率,事实上 System.out.printlnblocking - 就像 Java 中的大多数 I/O 调用(以及大多数其他环境)。这会完全扼杀吞吐量。
        【解决方案5】:

        原因不是服务器可能出现故障,而是可能很难在服务器上找到这样的输出。您应该始终使用某种具有已定义行为和输出文件的日志记录框架。

        【讨论】:

          【解决方案6】:

          一方面,有多个请求访问您的服务器并在System.out 上打印日志并不是一个好习惯。

          1. 所有日志都打印在屏幕上(文件描述符)。无法向后滚动并阅读日志。
          2. System.out 未同步。必须有一个并发管理来通过System.out管理打印
          3. 您无法通过System.out 确定日志级别。您不能即时将日志级别分隔为单独的输出。

          我希望这会有所帮助。

          【讨论】:

          • 关于第一点:很容易将输出重定向到一个文件,这样你就可以随意滚动了。
          • 如果您从控制台进行管道传输,那会占用大量资源。
          【解决方案7】:
          1. 程序将等待直到 println 完成。 记录器使用消息队列并仅在没有其他输出时才写入。
          2. System.out.println (SOP) 不是线程安全的(即异步) 记录器是线程安全的(即同步的)
          3. 记录器是高度可配置的 a. 格式化,限制记录器可实现的日志内容 b. 多目标日志记录——文件、控制台、数据库
          4. SOP 将日志写入服务器日志文件。 我们需要将应用程序日志与服务器日志分开,因为这可能会导致服务器故障
          5. 当在每个 SOP 中插入带有修复字符串的单个 SOP 时,每秒设法处理 1700 个事务的服务器应用程序下降到只有 800 个

          【讨论】:

            【解决方案8】:

            使用标准输出是不好的做法。但是,如果您有一个库或使用 System.out 和 System.err 的代码,您可以编写自己的 PrintStream 来记录线程名称和 info() 和 error() 文本。完成此操作后,您可能会更轻松地使用 System.out,因为它将写入日志,例如日志4j。

            理想情况下,您将直接使用正确的日志,尤其是调试级别的日志记录。恕我直言,只要您不使用内置的 System.out/err,它就没有那么重要了! (诚​​然,一个很大的假设)

            无论你使用 System.out 重定向到文件还是使用 log4j 或 Java Logger 写入文件,性能几乎完全相同。

            【讨论】:

              【解决方案9】:

              还有一个原因是 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,所以他们甚至不知道发生了什么事。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2013-10-10
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2013-01-27
                相关资源
                最近更新 更多