user2336377
user2336377

Reputation: 163

add Handler to every Logger

I want to add a custom handler to every logger for every class of my project. I have a logging.properties file, which is read at the very beginning using:

try (InputStream in = ReportingService.class.getResourceAsStream("logging.properties")) {
    LogManager.getLogManager().readConfiguration(in);
} catch (IOException ex) {
    Logger.getLogger(myClass.class.getName()).log(Level.SEVERE, null, ex);
}

The logging.properties file looks like this:

handlers=java.util.logging.ConsoleHandler,myPackage.AlertHandler
.level=SEVERE
java.util.logging.ConsoleHandler.level=SEVERE
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
myPackage.AlertHandler.level=SEVERE
myPackage.AlertHandler.formatter=java.util.logging.SimpleFormatter

And myPackage.AlertHandler.java looks like this:

package myPackage;
import java.util.logging.Handler;
import java.util.logging.LogRecord;
import javafx.scene.control.Alert;
public class AlertHandler extends Handler {
    @Override
    public void publish(LogRecord lr) {
        Alert a = new Alert(Alert.AlertType.ERROR);
        a.setTitle("Exception!");
        a.setHeaderText("Exception was thrown, here is the StackTrace:");
        a.setContentText(getFormatter().formatMessage(lr));
        Platform.runLater(()->{a.showAndWait();});
    }
    @Override
    public void flush() {
        //no real handler is open, nothing to flush
    }
    @Override
    public void close() throws SecurityException {
        //no real handler is open, nothing to close
    }
}

The logging file is read without issues, as no more INFO or WARNING messages are printed to the console, only SEVERE. But my custom handler is never called, as no Alert windows are ever opened. I also tried adding the handler to the global logger, in hopes of every other logger inheriting its handlers, but it doesn't work either:

Logger.getGlobal().addHandler(new AlertHandler());
Logger.getLogger("").addHandler(new AlertHandler());

Adding the handler to a specific logger works as intended: if an error is thrown anywhere in the class, an alert window is opened with the stacktrace.

Logger.getLogger("mySecondClass").addHandler(new AlertHandler());

But I want to add this handler to EVERY logger in any class!

Upvotes: 2

Views: 7440

Answers (1)

jmehrens
jmehrens

Reputation: 11045

But I want to add this handler to EVERY logger in any class!

If you want this handler to see all logger output then you just have to install one instance on the root logger. For example, Logger.getLogger("").addHandler(new AlertHander());

By default, child loggers are going to publish the log records to the parent handers.

The main problem is your code is going to always generate a NullPointerException because you never assigned a formatter to be used with the handler. So the call to getFormatter is going to return null and then fail.

public static void main(String[] args) {
    Handler h = new Handler() {

        @Override
        public void publish(LogRecord record) {
        }

        @Override
        public void flush() {
        }

        @Override
        public void close() throws SecurityException {
        }
    };
    System.out.println(h.getFormatter());
}

You also need to:

  1. Add code to parse the level, formatter, and filter assigned by the log manager for your handler.
  2. Choose sane defaults when no level, formatter, or filter values are defined.
  3. Deal with non-JavaFX Application Threads creating and showing the dialog by using Platform.runLater.
  4. Call isLoggable in your publish method to determine if your handler should publish or not.
  5. Catch runtime exceptions inside your publish method and track them by calling Handler.reportError.

Make life easy and create unit tests to ensure your handler actually works before you try to use it in the wild.

Upvotes: 6

Related Questions