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

发布于 2024-08-10 12:44:47 字数 1050 浏览 1 评论 0原文

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

尽管如此,应用程序开发人员似乎喜欢到处抛出 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

I maintain an application which acts as a container for multiple individual programs. These programs have their own dedicated logging facility, i.e. everything they log does to a special log file.

Nevertheless, application developers seem to love to throw System.out.println and e.printStackTrace calls all over, making it impossible to maintain a clean console when running the container.

How can I prevent these applications from polluting System.out and System.err?


Implementation notes:

  • the applications use Log4j for logging;
  • the container also uses the console for logging, but it is strictly reserved for lifecycle events and problems, so I still need the console;
  • the applications are loaded using a custom classloader, but no security checks are applied.

Update:

Simply redirecting System.out would not work since it redirects all output, so something like this fails:

    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");

This should succeed.

Update 2:

The applications are loaded and configured using code similar to

App app = new UrlClassLoader(...).loadClass(className)).newInstance();
app.setLogger(loggerForClass(app));

Log4j is loaded from the system class loader.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

迷你仙 2024-08-17 12:44:47

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

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

Assuming that you can control your containers output you can do the following:

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
鱼窥荷 2024-08-17 12:44:47

您可以使用 System.setOut()System.setErr()stdoutstderr 重定向到打印流

You can use System.setOut() and System.setErr() to redirect stdout and stderr to instances of PrintStream.

咽泪装欢 2024-08-17 12:44:47

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

基本上你可以设置新的流,要么通过管道传输到日志记录,要么只是让数据消失殆尽。我更喜欢后者,因为它可以立即消除开发人员对 System.out 的依赖,并避免他们在其中编写的任何内容都会消失。

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

While Java defines a standard System.out and System.err, these can be overwritten with your own streams. See http://www.devx.com/tips/Tip/5616

Basically you can set up new streams that either pipe to the logging, or simply let data flop off into nothingness. My preference would be the latter, as it would instantly cure developers from relying on System.out and err as anything they write there just disappears.

**Update:
I just reread your stipulations in the question and see you still need the console for the container application. This might still work if you write a wrapper around the standard stream so you can check each call and see if it is coming from the parent application (and pass it on) or a child application (and block it)

萌化 2024-08-17 12:44:47

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

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

Use aversion therapy. A visit from "The Inspectors" is scheduled whenever any code is checked in containing unpleasant constructs.

Nice cubicle you got ere, be shame if anyfing appened to it.
好久不见√ 2024-08-17 12:44:47

如果您有无头构建机制,ant 等,那么您可以将 CheckStyle 添加到构建中,并配置 checkstyle 以便在代码中发现任何 System.out.println 或 e.printStackTrace 时使构建失败。

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

If you have a headless build mechanism, ant or such like then you could add CheckStyle to the build and configure checkstyle to fail the build if it finds any System.out.println or e.printStackTrace in the code.

If you don't have a headless build I would recommend that you build one as it means you have repeatable, predictable builds.

-黛色若梦 2024-08-17 12:44:47

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

唯一棘手的部分实际上是能够检测什么是有效的调用,什么是无效的。一种有效但可能非常慢的方法是调用 Thread.currentThread().getStackTrace() 并查看哪些代码(或至少是包)正在调用您(只需返回 if这不是一个有效的)。但我不建议这样做,因为性能损失将是惊人的,特别是在每个字节读取时都这样做。

更好的想法可能是在所有有效的容器线程中设置 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,事情会变得更容易。您可以将所有控制台写入交给特定的工作人员;并让这个工作人员“窃取”System.out(将其存储在自己的字段中并直接使用它来写入输出),同时将实际的 System.out 设置为无操作写入器。

System.setOut will redirect all output - but the PrintStream you supply can decide how the output is handled. Thus I'm sure you could provide such a stream that would only actually print statements from your application.

The only tricky part really is being able to detect what's a valid call and what's not. A working, but likely very slow way to do this, would be to call Thread.currentThread().getStackTrace() and see what code (or package, at least) is calling you (simply returning if it's not a valid one). I wouldn't recommend this though as the performance hit would be staggering, especially doing this on every byte read.

A better idea might be to set a ThreadLocal flag in all of your valid, container threads. Then you can implement the PrintStream something like the following:

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
}

Alternatively, if you're able to change the printlns in the container code, things get easier. You can hand off all the console writes to a specific worker; and have this worker "steal" System.out (store it in its own field and use it directly for writing output) while setting the actual System.out to a no-op writer.

池木 2024-08-17 12:44:47

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

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

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

The key here is to configure log4j before redirecting the output streams, e.g.

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

System.out.println("I fail");
Logger.getLogger(...).info("I work");
弄潮 2024-08-17 12:44:47

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

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

(为了获得额外的奖励,请抛出 OutOfMemoryException ;-))

Convert the System.out and System.err streams to special implementations that throw a RuntimeException("Use logging instead of System.out") every time a character is written.

If your container is important, they will get the idea pretty quickly :)

(For extra bonus throw OutOfMemoryException instead ;-))

開玄 2024-08-17 12:44:47

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

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

What I have done is redirect the PrintStream for System.out and System.err to commons-logging as INFO and ERROR level logging respectively.

This gets trickier if you want some threads to be able to write to the console or you want logs to go to the console as well but it can be done.

许你一世情深 2024-08-17 12:44:47

在替换它们之前,您实际上可以获取并存储 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,并在输出的每一行前面加上它来自的方法名称/行号。

You can actually get and store System.out/err before replacing them.

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");

I've used this to intercept all the System.out.println's in the codebase and prefix each line of output with the method name/line number it came from.

送君千里 2024-08-17 12:44:47

关闭 System.out 和 System.err 流。

Close the System.out and System.err streams.

十二 2024-08-17 12:44:47

我们使用 log4j 技巧,但将日志记录到单独的文件(stdout.log、stderr.log)。将它们的输出与真正理解日志记录的部分混合在一起是没有用的......

We use the log4j trick but log to separate files (stdout.log, stderr.log). It's not useful to have their output mixed in with the parts that actually understand logging...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文