How do I change java logging console output from std err to std out?
Asked Answered
L

13

45

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)?

Landlord answered 11/10, 2008 at 15:0 Comment(0)
B
18

I've arrived at

SimpleFormatter fmt = new SimpleFormatter();
StreamHandler sh = new StreamHandler(System.out, fmt);
logger.addHandler(sh);
Benzoate answered 19/1, 2011 at 18:9 Comment(1)
This does not disable the default handler. Just follow the answer https://mcmap.net/q/215955/-how-can-i-disable-the-default-console-handler-while-using-the-java-logging-apiTatiania
B
14

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!

Bronchoscope answered 18/3, 2011 at 20:38 Comment(0)
L
10

I figured out one way. First remove the default console handler:

setUseParentHandlers(false);

Then subclass ConsoleHandler and in the constructor:

setOutputStream(System.out);
Landlord answered 11/10, 2008 at 23:49 Comment(2)
You better wrap System.out with self implemented OutputStream to avoid closing it on the next call to setOutputStream(...) or LogManager.reset().Sclerenchyma
Also, take care to not call the super constructor (of 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
G
10
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() {
    }
};
Garbanzo answered 25/5, 2010 at 15:46 Comment(1)
But, in the modern days... is there a way to do this without implementing a custom Handler??Pernicious
S
7

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.)

Subbasement answered 18/5, 2014 at 0:38 Comment(1)
Perfect. Has to be said that the designers haven't exactly made it easy... why can't they anticipate this need? Mind you it seems to be the same with other logging frameworks: your first task? Jump through several difficult hoops!Morra
S
5

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();
     }
}
Scutter answered 3/6, 2020 at 4:22 Comment(1)
I can't see logs until application is closed. Do you have any idea how to see logs in realtime?Haar
G
4

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.

Glue answered 11/10, 2008 at 15:24 Comment(2)
I think ConsoleHandler is the default, there is a StreamHandler that can print to any other stream.Nigrescent
Yes, but you'd want to subclass StreamHandler so as to avoid trying to close System.err, I suspect.Glue
N
3

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.

Nigrescent answered 11/10, 2008 at 15:29 Comment(0)
R
3

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.

Reisch answered 18/4, 2012 at 15:53 Comment(0)
S
3

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);
    }
};
Stockwell answered 25/2, 2017 at 16:14 Comment(0)
D
2

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.

Demodulate answered 10/3, 2016 at 14:48 Comment(0)
B
1

If you set setUseParentHandlers(false); only THAT class has it set. Other classes in the app will still pass it thru to stderr.

Barley answered 18/12, 2008 at 22:59 Comment(1)
If one does that for the root logging class (Logger.GLOBAL_LOGGER_NAME), console logging is disabled for all classes.Tatiania
G
-2

Simply extend StreamHandler & in the constructor call Super(System.out,). This will avoid closing System.err - Thanks

Gaynell answered 21/10, 2009 at 22:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.