I'm using the standard ConsoleHandler
from java.util.logging
and by default the console output is directed to the error stream (i.e. System.err
).
How do I change the console output to the output stream (i.e. System.out
)?
I'm using the standard ConsoleHandler
from java.util.logging
and by default the console output is directed to the error stream (i.e. System.err
).
How do I change the console output to the output stream (i.e. System.out
)?
I've arrived at
SimpleFormatter fmt = new SimpleFormatter();
StreamHandler sh = new StreamHandler(System.out, fmt);
logger.addHandler(sh);
Hmm I just got bit in the foot a few times, trying to accomplish this feat. Before googling my way here I managed to conjure the following hack. Ugly, but it seems to get the job done.
public class StdoutConsoleHandler extends ConsoleHandler {
protected void setOutputStream(OutputStream out) throws SecurityException {
super.setOutputStream(System.out); // kitten killed here :-(
}
}
Watch out: Calling setOutputStream() from the constructor is tempting, but it does (as Jon Skeet already pointed out) close System.err. Mad skills!
I figured out one way. First remove the default console handler:
setUseParentHandlers(false);
Then subclass ConsoleHandler and in the constructor:
setOutputStream(System.out);
System.out
with self implemented OutputStream
to avoid closing it on the next call to setOutputStream(...)
or LogManager.reset()
. –
Sclerenchyma ConsoleHandler
) since it sets System.err and you will close it when calling setOutputStream(System.out)
. Or, you can just subclass StreamHandler
and call super( OutputStream, Formatter)
. –
Sclerenchyma Handler consoleHandler = new Handler() {
@Override
public void publish(LogRecord record) {
if (getFormatter() == null) {
setFormatter(new SimpleFormatter());
}
try {
String message = getFormatter().format(record);
if (record.getLevel().intValue() >= Level.WARNING.intValue()) {
System.err.write(message.getBytes());
} else {
System.out.write(message.getBytes());
}
} catch (Exception exception) {
reportError(null, exception, ErrorManager.FORMAT_FAILURE);
}
}
@Override
public void close() throws SecurityException {
}
@Override
public void flush() {
}
};
I had a similar problem. I wanted to log INFO and below to System.out
, and WARNING and above to System.err
. Here is the solution I implemented:
public class DualConsoleHandler extends StreamHandler {
private final ConsoleHandler stderrHandler = new ConsoleHandler();
public DualConsoleHandler() {
super(System.out, new SimpleFormatter());
}
@Override
public void publish(LogRecord record) {
if (record.getLevel().intValue() <= Level.INFO.intValue()) {
super.publish(record);
super.flush();
} else {
stderrHandler.publish(record);
stderrHandler.flush();
}
}
}
Of course, you could make it more flexible by factoring out the hard-coded reference to Level.INFO
, for example. But this worked well for me to get some basic dual-stream logging. (BTW, the tips about not subclassing ConsoleHandler to avoid closing the System.err
were very useful.)
Step 1: Set parent handlers to false.
log.setUseParentHandlers(false);
Step 2: Add a handler that writes to System.out
log.addHandler(new StreamHandler(System.out, new SimpleFormatter()));
Thats it..
import java.io.IOException;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
import java.util.logging.StreamHandler;
public class App {
static final Logger log = Logger.getLogger("com.sample.app.App");
static void processData() {
log.info("Started Processing Data");
log.info("Finished processing data");
}
public static void main(String args[]) throws IOException {
log.setUseParentHandlers(false);
log.addHandler(new StreamHandler(System.out, new SimpleFormatter()));
processData();
}
}
Have a look at the docs and source for ConsoleHandler - I'm sure you could easily write a version which just uses System.err instead of System.out. (It's a shame that ConsoleHandler doesn't allow this to be configured, to be honest.)
Then it's just a case of configuring the logging system to use your new StdoutHandler (or whatever you call it) in the normal way.
If you use Java logging, you can change the default handler:
For example, for files:
Handler fh = new FileHandler(FILENAME);
Logger.getLogger(LOGGER_NAME).addHandler(fh);
If you want to output to a stream you can use StreamHandler, I think you can configure it with any output stream that you woud like, including the system stream.
If there is still someone out there looking for a solution to this problem. Here's what I came up with finally: I just subclassed StreamHandler and added an additional parameter MaxLevel, which is checked at the beginning of publish(). If the level of the logging event is larger than MaxLevel, publish won't be executed any further. Here are the details:
MaxlevelStreamHandler.java Main Class below.
package helper;
/**
* The only difference to the standard StreamHandler is
* that a MAXLEVEL can be defined (which then is not published)
*
* @author Kai Goergen
*/
import java.io.PrintStream;
import java.util.logging.Formatter;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.StreamHandler;
public class MaxlevelStreamHandler extends StreamHandler {
private Level maxlevel = Level.SEVERE; // by default, put out everything
/**
* The only method we really change to check whether the message
* is smaller than maxlevel.
* We also flush here to make sure that the message is shown immediately.
*/
@Override
public synchronized void publish(LogRecord record) {
if (record.getLevel().intValue() > this.maxlevel.intValue()) {
// do nothing if the level is above maxlevel
} else {
// if we arrived here, do what we always do
super.publish(record);
super.flush();
}
}
/**
* getter for maxlevel
* @return
*/
public Level getMaxlevel() {
return maxlevel;
}
/**
* Setter for maxlevel.
* If a logging event is larger than this level, it won't be displayed
* @param maxlevel
*/
public void setMaxlevel(Level maxlevel) {
this.maxlevel = maxlevel;
}
/** Constructor forwarding */
public MaxlevelStreamHandler(PrintStream out, Formatter formatter) {
super(out, formatter);
}
/** Constructor forwarding */
public MaxlevelStreamHandler() {
super();
}
}
Main Class
To now show some events in stdout and some in stderr, simply setup two StreamLoggers, one for critical events and one for all others, and disable the standard console logger:
// setup all logs that are smaller than WARNINGS to stdout
MaxlevelStreamHandler outSh = new MaxlevelStreamHandler(System.out, formatter);
outSh.setLevel(Level.ALL);
outSh.setMaxlevel(Level.INFO);
logger.addHandler(outSh);
// setup all warnings to stdout & warnings and higher to stderr
StreamHandler errSh = new StreamHandler(System.err, formatter);
errSh.setLevel(Level.WARNING);
logger.addHandler(errSh);
// remove default console logger
logger.setUseParentHandlers(false);
logger.info("info");
logger.warning("warning");
logger.severe("severe");
Hope this helps!
Update: I added super.flush() right after super.publish() to make sure that the message is shown immediately. Before, I had problems that the log-messages were always shown at the end. It's now part of the code above.
When we create a new ConsoleHandler object, default output stream is "system.err". Sadly Java doesn't provide any public method for ConsoleHandler class to set output stream. So it can be set only at the time of object creation. As ConsoleHandler class extends StreamHandler, which has a protected method "setOutputStream" to set output stream explicitly. To set output Stream for ConsoleHandler just override this method at the time of new call for object creation.
ConsoleHandler consoleHandler = new ConsoleHandler() {
@Override
protected synchronized void setOutputStream(OutputStream out) throws SecurityException {
super.setOutputStream(System.out);
}
};
The ConsoleHandler will grab a snapshot of System.err
during construction. One option would be to swap the global error stream with the global out stream and then create the ConsoleHandler.
ConsoleHandler h = null;
final PrintStream err = System.err;
System.setErr(System.out);
try {
h = new ConsoleHandler(); //Snapshot of System.err
} finally {
System.setErr(err);
}
This assumes that the code has permission to modify error stream and that no other running code is accessing the error stream. In short, this is an option but there are safer alternatives.
If you set setUseParentHandlers(false); only THAT class has it set. Other classes in the app will still pass it thru to stderr.
Simply extend StreamHandler & in the constructor call Super(System.out,). This will avoid closing System.err - Thanks
© 2022 - 2024 — McMap. All rights reserved.