16

I have a specialized logger class which uses the java.util.logging.Logger class. I want to be able to use this logger in the shutdown hook of another class. However, it doesn't seem to log at shutdown. From what I've read, there may already be a shutdown hook activated for the logger itself which is causing the issue.

How can I get this to work? Ideally, I would like it to be seen in the log file that I did in fact execute the shutdown hook when the process terminated.

monterinio
  • 49
  • 2
  • 11

2 Answers2

20

Again looking at the source, the solution appears to be to define a system property java.util.logging.manager which is a subclass of LogManager which overrides the reset(); method so the Loggers continue to work on shutdown.

import java.util.logging.LogManager;
import java.util.logging.Logger;

public class Main {
    static {
        // must be called before any Logger method is used.
        System.setProperty("java.util.logging.manager", MyLogManager.class.getName());
    }

    public static class MyLogManager extends LogManager {
        static MyLogManager instance;
        public MyLogManager() { instance = this; }
        @Override public void reset() { /* don't reset yet. */ }
        private void reset0() { super.reset(); }
        public static void resetFinally() { instance.reset0(); }
    }

    public static void main(String... args) {
        Logger logger1 = Logger.getLogger("Main1");
        logger1.info("Before shutdown");
        Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Logger logger2 = Logger.getLogger("Main2");
                    logger2.info("Shutting down 2");

                } finally {
                    MyLogManager.resetFinally();
                }
            }
        }));
    }
}

prints

Dec 11, 2012 5:56:55 PM Main main
INFO: Before shutdown
Dec 11, 2012 5:56:55 PM Main$1 run
INFO: Shutting down 2

From this code for LogManager, you can see see there is a shutdown hook which dismantles the handlers and closes them. Logger only works in shutdown if it hasn't been used before so this code is not run.

// This private class is used as a shutdown hook.
// It does a "reset" to close all open handlers.
private class Cleaner extends Thread {

    private Cleaner() {
        /* Set context class loader to null in order to avoid
         * keeping a strong reference to an application classloader.
         */
        this.setContextClassLoader(null);
    }

    public void run() {
        // This is to ensure the LogManager.<clinit> is completed
        // before synchronized block. Otherwise deadlocks are possible.
        LogManager mgr = manager;

        // If the global handlers haven't been initialized yet, we
        // don't want to initialize them just so we can close them!
        synchronized (LogManager.this) {
            // Note that death is imminent.
            deathImminent = true;
            initializedGlobalHandlers = true;
        }

        // Do a reset to close all active handlers.
        reset();
    }
}


/**
 * Protected constructor.  This is protected so that container applications
 * (such as J2EE containers) can subclass the object.  It is non-public as
 * it is intended that there only be one LogManager object, whose value is
 * retrieved by calling Logmanager.getLogManager.
 */
protected LogManager() {
    // Add a shutdown hook to close the global handlers.
    try {
        Runtime.getRuntime().addShutdownHook(new Cleaner());
    } catch (IllegalStateException e) {
        // If the VM is already shutting down,
        // We do not need to register shutdownHook.
    }
}

From my own testing

Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
    @Override
    public void run() {
        try {
            Logger logger2 = Logger.getLogger("Main2");
            logger2.info("Shutting down 2");
        } catch (Throwable t) {
            t.printStackTrace();
        }
    }
}));

prints

Dec 11, 2012 5:40:15 PM Main$1 run
INFO: Shutting down 2

but if you add

Logger logger1 = Logger.getLogger("Main1");

outside this block you get nothing.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • 3
    Even using `Logger.getLogger(name) I still don't see anything printed to my log. However, setting the System property and copying over the MyLogManager code I was able to get it working. Beautiful, thank you! –  Dec 11 '12 at 18:32
  • I used that MyLogManager approach, but the result of that was hat all logs were suddenly duplicated, every log line was written twice. I traced it and verified that the MyLogManager got instantiated only once. As soon as I commented out setting that system property all was back to normal, i.e., each log line being written only once no logging in shutdownHook. – Gunther Schadow May 19 '20 at 14:33
0

Does the shutdown hook has any advantage?

You better, add the Log output in the last line in main: The finally block will be executed in all cases, except when the VM crashes but then the shutdown hook will not be executed, too.

static void main(String[] argv) {

  try {
    startApp();
  } finally {
    LOGGER.info("shudown");
  }
}
Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
AlexWien
  • 28,470
  • 6
  • 53
  • 83
  • My process is one that is live all day - sitting and waiting for something to happen. Not a one and done type thing where I can just put what I need to at the end of the code. I do believe a shutdown hook is the right solution –  Dec 11 '12 at 18:09
  • 1
    So it seems that you dont know where your end of code is? You should know that, and find out. This has nothing to do with one shot! Our sw runs for weeks without shutdown, but we know where the end is. Maybe it is a bit difficult, using spring or another framework. But you should know to clean up, because often an ordered shutdoiwn is necessary! The reason why LOGGING needs a shutdown handler is that its a lib and cannot knwo the "end of code" – AlexWien Dec 11 '12 at 19:03
  • 2
    The process doesn't exit on its own, so similarly there is no logical end. –  Dec 11 '12 at 19:34
  • In a more complex program, a shutdown hook might be used to call some cleaning-up code which might have an interest in logging the result of this clean-up. – Thomas Dec 02 '15 at 12:37
  • 1
    Finally block won't be executed in case something inside application logic calls, for example, System.exit(1). As I can see, @Marianna's problem can be solved reliably only using external monitoring/logging tool... – Alexander Radchenko Jul 09 '19 at 11:28