At runtime I often create/modify log4j Loggers, Appenders, Levels, Layouts, and time to time need to reset everything back to defaults.
Log4j system has well defined Default Initialization Procedure that is executed when log4j classes are loaded into memory. Is there any way to re-execute the entire Procedure programmatically later at runtime?
I found several resetConfiguration()
methods in log4j documentation, but not sure if any of them will do what the Default Initialization Procedure does:
BasicConfigurator.resetConfiguration();
Hierarchy.resetConfiguration();
LogManager.resetConfiguration();
Any other suggestions on resetting log4j configurations are more then welcome! Thank you.
This question is related to skiphoppy's question which I answered earlier today. The bounty question he added to this one here calls for a more tricky solution than Jan Zyka's:
Because the default initialisation is a hard-coded static block executed only once during class-loading of
LogManager
, you need AOP (aspect-oriented programming), more specifically AspectJ in order to intercept the static initialiser. I explained how to do that in my answer to skiphoppy's other question.Okay, so now we can intercept the static initialisation and trick LogManager into telling us the URL, but in order to re-execute the whole code block we need another trick called a worker object pattern. Here is the sample code, the explanation follows:
Sample application class:
Aspect intercepting static initialisation of
LogManager
:How it works:
It is beyond the scope of this answer to explain the general concept of AOP, so I assume you know it or are going to read something in order to understand it.
Log4jAspect
interceptsLogManager
's static initialisation in anaround()
advice.proceed()
call (i.e. the execution of the static initialisation) is packaged inside a worker object implemented by an anonymousRunnable
instance. This effectively wraps the call into an object with arun()
method which can be issued at will. (Aha, here is our trick! In more dynamic languages like Scala you would call this a lexical scope.)Runnable
instance to another class's public static member so it becomes accessible outside the aspect.run()
method.So far, so good, now the
LogManager
class has been loaded and initialised properly, just as if no aspect did exist. But now look atLog4jDemo.main
:run()
method.If you use command line argument
-Dlog4j.debug=true
, you will see something like this:Tadaa! As you can see, the default initialisation has indeed been executed twice. The log output proves it. For example you see
Using URL [file:/(...)]
twice in the log.Conclusion:
While this is not a particularly nice way to re-issue log4j default initialisation compared to the more desireable situation that it not be hard-coded but exposed to the user via an API call, the facts are as they are and we need this trick. I do doubt that it should be necessary to re-run the full default initialisation block in any given situation, but because the question was asked, I wanted to answer it precisely and not by suggesting a workaround. Enjoy!
According to the documentation for the
doConfigure
method:Read configuration from a file. The existing configuration is not cleared nor reset. If you require a different behavior, then call
resetConfiguration
method before calling doConfigure.So I belive that calling
LogManager.resetConfiguration()
and recallPropertyConfigurator.configure()
with the same files as on startup will do what you want.The
resetConfiguration()
method is documented in Hierarchy class.Jan Zyka's solution pointed me in the right direction, but I'm using an XML configuration file rather than a properties files. Here is the code that worked for me:
I get a properly formatted log4j log entry with "abc123" as the log message.