15

我正在用 Java 编写一个模拟重力的程序,其中我有一堆日志语句(到 System.out)。我的程序运行得很慢,我认为日志记录可能是部分原因。有什么方法可以禁用 System.out,这样我的程序在打印时不会变慢,还是我必须手动检查/取消注释每个以启用/禁用调试语句?

4

10 回答 10

22

输出流 gobbler 再次可以工作,可能类似于...

System.setOut(new PrintStream(new OutputStream() {
    @Override
    public void write(int arg0) throws IOException {

    }
}));

但是更好的解决方案是使用已经提到的正式的日志记录实用程序。

于 2012-03-27T02:22:37.787 回答
13

您应该使用真正的日志框架,例如log4jslf4j。在任何一种情况下,您都可以设置日志记录级别,以限制转储到日志中的信息级别。

如果您坚持使用 System.out,您可以将日志记录语句包装在一个条件中,例如,

if (DEBUG) {
  System.out.println(...);
}

是的,大量记录或打印到 System.out 会影响性能。

于 2012-03-27T02:19:41.797 回答
7

我同意其他人的观点,即应该使用适当的记录器。然而,这并非在所有情况下都是可能的。以下代码禁用并且按照OP的要求快速:

System.setOut(new java.io.PrintStream(new java.io.OutputStream() {
    @Override public void write(int b) {}
}) {
    @Override public void flush() {}
    @Override public void close() {}
    @Override public void write(int b) {}
    @Override public void write(byte[] b) {}
    @Override public void write(byte[] buf, int off, int len) {}
    @Override public void print(boolean b) {}
    @Override public void print(char c) {}
    @Override public void print(int i) {}
    @Override public void print(long l) {}
    @Override public void print(float f) {}
    @Override public void print(double d) {}
    @Override public void print(char[] s) {}
    @Override public void print(String s) {}
    @Override public void print(Object obj) {}
    @Override public void println() {}
    @Override public void println(boolean x) {}
    @Override public void println(char x) {}
    @Override public void println(int x) {}
    @Override public void println(long x) {}
    @Override public void println(float x) {}
    @Override public void println(double x) {}
    @Override public void println(char[] x) {}
    @Override public void println(String x) {}
    @Override public void println(Object x) {}
    @Override public java.io.PrintStream printf(String format, Object... args) { return this; }
    @Override public java.io.PrintStream printf(java.util.Locale l, String format, Object... args) { return this; }
    @Override public java.io.PrintStream format(String format, Object... args) { return this; }
    @Override public java.io.PrintStream format(java.util.Locale l, String format, Object... args) { return this; }
    @Override public java.io.PrintStream append(CharSequence csq) { return this; }
    @Override public java.io.PrintStream append(CharSequence csq, int start, int end) { return this; }
    @Override public java.io.PrintStream append(char c) { return this; }
});

我的样本测量结果是:

  • 82ms 完全输出
  • 仅覆盖时write(int)为57 毫秒OutputStream
  • 覆盖PrintStream方法时也是31 毫秒
  • 注释掉所有printlnprintf用法时为 5 毫秒

因此,它比这里的其他答案更快,但不能击败不存在的代码(注释掉System.outs 或换行if (DEBUG)),因为 varargs 分配数组和框原语或StringBuilder+也)仍然执行。

于 2016-01-17T14:03:04.807 回答
3

绝对开始使用日志 API 而不是System.out. 我强烈推荐Logback。您可以轻松设置仅记录到控制台但可以随时关闭的内容。Logback 配置起来非常简单,但如果您不喜欢配置文件,您甚至可以通过编程方式进行设置。

Log4j也不错,它是与 Logback 同一作者的较早框架,被许多人认为是标准。 SLF4J再次由同一作者撰写,它不是真正的日志框架,而是抽象实际日志实现的标准包装器。Log4j 可以封装在 SLF4J 中,Logback 有原生的 SLF4J 接口。如果我是你,出于各种原因,我会避免使用 Apache Commons Logging 。我从来都不是内置 Java 日志记录 ( java.util.logging) 的忠实粉丝。

于 2012-03-27T02:20:18.677 回答
2

我建议您将来研究 log4j(或类似的库)。它的工作方式与 system.out 相同,但实际上开关可以在需要时将其关闭。

于 2012-03-27T02:20:11.060 回答
2

如果您的应用程序是在命令行启动的,您可以在操作系统级别禁用整个程序的标准输出流。这将有效地禁用所有 System.out.prinln(),而无需您更改任何代码。

在 Linux/Unix 上,您可以通过将 stdout 重定向到 /dev/null 来关闭它:java MyJavaProgram > /dev/null

于 2012-03-27T02:43:26.343 回答
1

如果您不介意只浏览一次,您可以创建一个“启用”或“禁用”的常量,并添加一个标志以仅在启用该常量时打印。然后你可以很容易地翻转常数。

public static boolean PRINTLN_ENABLED = true;

if(Constant.PRINTLN_ENABLED)
    System.out.println();
于 2012-03-27T02:19:39.753 回答
1

由于您在其中一条评论中提到了 Eclipse,您可以在此处找到相关答案:https ://stackoverflow.com/a/799277/1017130

(我最初想将此作为对 shj 答案的评论发布,但似乎我首先需要更多代表)

最好的长期解决方案是使用日志框架。我通常使用内置的日志记录,因为我尽量避免额外的依赖。您可以在此处阅读更多信息:http: //docs.oracle.com/javase/1.4.2/docs/guide/util/logging/overview.html

于 2012-03-27T07:09:28.853 回答
0

我怀疑您可以使用System.setOut(PrintStream)委派给一个PrintStream不做任何事情的人,但这可能不会消除您的所有开销——例如,我怀疑错误消息的格式仍然会花费您。

于 2012-03-27T02:21:38.850 回答
0

我已将此代码放在我的 Web 项目的查找侦听器中一次并解决了问题

  /*
     * In this case, if you are not in debug mode or any other 
     *the default system out is replaced internally with this
     *implementation, else it works as expected. This way you do not 
     *have to find and replace anything in your code.
     */
//or as i did servletContext.getInitParameter("mode") 
//from web.xml for global parameter

        if(!DEBUG) {
        System.setOut(
            new PrintStream(new OutputStream() { 
                public  void    close() {}
                public  void    flush() {}
                public  void    write(byte[] b) {}
                public  void    write(byte[] b, int off, int len) {}
                public  void    write(int b) {}

            } );
        }
    }
于 2013-09-14T08:58:23.160 回答