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.
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.
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...
You can actually get and store System.out/err before replacing them.
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.
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 ;-))
You can use
System.setOut()
andSystem.setErr()
to redirectstdout
andstderr
to instances ofPrintStream
.Use aversion therapy. A visit from "The Inspectors" is scheduled whenever any code is checked in containing unpleasant constructs.