在java中删除对System.out的访问权限

时间:2009-11-10 20:20:31

标签: java logging log4j

我维护一个应用程序,它充当多个单独程序的容器。这些程序有自己的专用日志记录工具,即他们记录的所有内容都会记录到特殊的日志文件中。

然而,应用程序开发人员似乎喜欢全部抛出System.out.printlne.printStackTrace调用,这使得在运行容器时无法维护干净的控制台。

如何防止这些应用程序污染System.outSystem.err


实施说明:

  • 应用程序使用Log4j进行日志记录;
  • 容器也使用控制台进行日志记录,但它严格保留用于生命周期事件和问题,因此我仍然需要控制台;
  • 使用自定义类加载器加载应用程序,但不应用安全检查。

更新

简单地重定向System.out不起作用,因为它重定向所有输出,所以这样的事情失败了:

    System.setOut(new PrintStream(new OutputStream() {

        @Override
        public void write(int b) {

            throw new Error("Not on my watch you don't");

        }
    }));

    Logger logger = Logger.getLogger(Runner.class);
    logger.info("My log message");

这应该会成功。

更新2:

使用类似于

的代码加载和配置应用程序
App app = new UrlClassLoader(...).loadClass(className)).newInstance();
app.setLogger(loggerForClass(app));

从系统类加载器加载Log4j。

12 个答案:

答案 0 :(得分:12)

假设您可以控制容器输出,您可以执行以下操作:

import java.io.*;
public class SysOut {
    public static void main(String[] args) throws Exception {
            PrintStream pw = new PrintStream(new FileOutputStream("a.txt"));
            PrintStream realout = System.out;
            System.setOut(pw);
            System.out.println("junk");
            realout.print("useful");
}
}

$ java SysOut 
useful
$ cat a.txt 
junk

答案 1 :(得分:10)

您可以使用System.setOut()System.setErr()stdoutstderr重定向到PrintStream的实例。

答案 2 :(得分:6)

虽然Java定义了标准的System.out和System.err,但这些可以用您自己的流覆盖。见http://www.devx.com/tips/Tip/5616

基本上,您可以设置新的流,这些流可以管道到日志记录,也可以简单地让数据变为虚无。我的偏好是后者,因为它会立即使开发人员免于依赖System.out而错误,因为他们在那里写的任何内容都会消失。

**更新: 我只是重新阅读了问题中的规定,看到你仍然需要容器应用程序的控制台。如果您在标准流周围编写一个包装器,这可能仍然有效,这样您就可以检查每个调用并查看它是来自父应用程序(并将其传递)还是子应用程序(并阻止它)

答案 3 :(得分:5)

使用厌恶疗法。每当检查包含令人不快的构造的任何代码时,就会安排“检查员”的访问。

Nice cubicle you got ere, be shame if anyfing appened to it.

答案 4 :(得分:3)

如果你有一个无头构建机制,ant或类似的那样你可以将CheckStyle添加到构建中并配置checkstyle以使构建失败,如果它在代码中找到任何System.out.println或e.printStackTrace。

如果您没有无头构建,我建议您构建一个,因为这意味着您具有可重复的,可预测的构建。

答案 5 :(得分:3)

System.setOut 重定向所有输出 - 但您提供的PrintStream可以决定如何处理输出。因此,我确信您可以提供只会实际打印应用程序语句的流。

唯一棘手的部分实际上是能够检测到什么是有效的呼叫,什么不是。一个工作但可能非常慢的方法是调用Thread.currentThread().getStackTrace()并查看至少在调用您的代码(或包)(如果它不是有效代码则返回)。我不建议这样做,因为性能损失将是惊人的,特别是在读取的每个字节上执行此操作。

更好的想法可能是在所有有效的容器线程中设置ThreadLocal标志。然后,您可以实现类似以下内容的PrintStream:

public class ThreadValidity extends ThreadLocal<Boolean>
{
    private static final INSTANCE = new ThreadValidity();

    @Override Boolean initialValue() { return false; }
    public static ThreadValidity getInstance() { return INSTANCE; }
}

class VerifyingPrintStream extends PrintStream
{
    private boolean isValidThread()
    {
        return ThreadValidity.instance().get();
    }

    public void println(String s)
    {
        if (!isValidThread()) return;
        super.println(s);
    }

    public void println(Object o)
    {
        if (!isValidThread()) return;
        super.println(o);
    }

    // etc
}

或者,如果您能够更改容器代码中的println,事情会变得更容易。您可以将所有控制台写入权交给特定的工作人员;并让这个worker“窃取”System.out(将它存储在自己的字段中并直接用它来写输出),同时将实际的System.out设置为无操作的编写器。

答案 6 :(得分:2)

这里的关键是在重定向输出流之前配置log4j ,例如

BasicConfigurator.configure();
System.setOut(...);
System.setErr(...);

System.out.println("I fail");
Logger.getLogger(...).info("I work");

答案 7 :(得分:1)

将System.out和System.err流转换为每次写入字符时抛出RuntimeException(“使用日志而不是System.out”)的特殊实现。

如果你的容器很重要,他们会很快得到这个想法:)

(对于额外的奖励抛出OutOfMemoryException而来; - ))

答案 8 :(得分:1)

我所做的是将PrintStream for System.out和System.err重定向到commons-logging,分别记录为INFO和ERROR级别。

如果您希望某些线程能够写入控制台,或者您希望日志也可以转到控制台,那么这会变得更加棘手。但是可以这样做。

答案 9 :(得分:1)

在替换之前,您实际上可以获取并存储System.out / err。

OutputStream out=System.getOut();  // I think the names are right
System.setOut(some predefined output stream, null won't work);
out.println("Hey, this still goes to the output");
System.out.println("Oh noes, this does not");

我用它来拦截代码库中的所有System.out.println,并在输出的每一行前面加上它来自的方法名称/行号。

答案 10 :(得分:1)

关闭System.out和System.err流。

答案 11 :(得分:0)

我们使用log4j技巧但登录分离文件(stdout.log,stderr.log)。将输出与实际了解日志记录的部分混合起来是没有用的......