Excessive logging is a common cause of performance degrade of application. It is one of best practices to ensure proper logging within your Java EE application implementation. However, be careful with the logging level that you enable in your production environment. Excessive logging will trigger high IO on your server and increase CPU utilization. This can especially be a problem for older environments using older hardware or environments dealing with very heavy concurrent volumes.
A balanced approach is to implement a "reloadable logging level" facility to turn extra logging ON / OFF when required in your day to day production support.
Lets see how this can be done using WatchService provided by Java 7.
Step 1) Implements a WatchService which will listen for changes in given log4j file
Below given code initializes a thread which continuously watch for modifications in given log4j file [StandardWatchEventKinds.ENTRY_MODIFY]. As soon as a file change is observed, configurationChanged() method is invoked and DOMConfigurator.configure() method is used to again reload the log4j configuration.
Log4jChangeWatcherService.java
package com.howtodoinjava.demo; import java.io.IOException; import java.nio.file.FileSystems; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardWatchEventKinds; import java.nio.file.WatchEvent; import java.nio.file.WatchKey; import java.nio.file.WatchService; import org.apache.log4j.xml.DOMConfigurator; public class Log4jChangeWatcherService implements Runnable { private String configFileName = null; private String fullFilePath = null; public Log4jChangeWatcherService(final String filePath) { this.fullFilePath = filePath; } //This method will be called each time the log4j configuration is changed public void configurationChanged(final String file) { System.out.println("Log4j configuration file changed. Reloading logging levels !!"); DOMConfigurator.configure(file); } public void run() { try { register(this.fullFilePath); } catch (IOException e) { e.printStackTrace(); } } private void register(final String file) throws IOException { final int lastIndex = file.lastIndexOf("/"); String dirPath = file.substring(0, lastIndex + 1); String fileName = file.substring(lastIndex + 1, file.length()); this.configFileName = fileName; configurationChanged(file); startWatcher(dirPath, fileName); } private void startWatcher(String dirPath, String file) throws IOException { final WatchService watchService = FileSystems.getDefault().newWatchService(); //Define the file and type of events which the watch service should handle Path path = Paths.get(dirPath); path.register(watchService, StandardWatchEventKinds.ENTRY_MODIFY); Runtime.getRuntime().addShutdownHook(new Thread() { public void run() { try { watchService.close(); } catch (IOException e) { e.printStackTrace(); } } }); WatchKey key = null; while (true) { try { key = watchService.take(); for (WatchEvent< ?> event : key.pollEvents()) { if (event.context().toString().equals(configFileName)) { //From here the configuration change callback is triggered configurationChanged(dirPath + file); } } boolean reset = key.reset(); if (!reset) { System.out.println("Could not reset the watch key."); break; } } catch (Exception e) { System.out.println("InterruptedException: " + e.getMessage()); } } } }
2) Add Log4jConfigurator, an interface for your application to work with log4j
This class is a utility class which separates the log4j initialization code and reloading strategy from application code.
Log4jConfigurator.java
package com.howtodoinjava.demo; public class Log4jConfigurator { //This ensures singleton instance of configurator private final static Log4jConfigurator INSTANCE = new Log4jConfigurator(); public static Log4jConfigurator getInstance() { return INSTANCE; } //This method will start the watcher service of log4j.xml file and also configure the loggers public void initilize(final String file) { try { //Create the watch service thread and start it. //I will suggest to use some logic here which will check if this thread is still alive; //If thread is killed then restart the thread Log4jChangeWatcherService listner = new Log4jChangeWatcherService(file); //Start the thread new Thread(listner).start(); } catch (Exception e) { e.printStackTrace(); } } }
3) Test the log4j reload events
To test the code, I am logging two two statements: one debug level and one info level. Both statements are logged after 2 seconds in a loop. I will change the logging level after few log statements and the log4j should be reloaded.
log4j-config.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd"> <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"> <appender name="console" class="org.apache.log4j.ConsoleAppender"> <param name="Target" value="System.out"/> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="[%t] %-5p %c %x - %m%n"/> </layout> </appender> <root> <priority value ="info" /> <appender-ref ref="console" /> </root> </log4j:configuration>
Log4jConfigReloadExample.java
package com.howtodoinjava.demo; import org.apache.log4j.Logger; public class Log4jConfigReloadExample { private static final String LOG_FILE_PATH = "C:/Lokesh/Setup/workspace/Log4jReloadExample/log4j-config.xml"; public static void main(String[] args) throws InterruptedException { //Configure logger service Log4jConfigurator.getInstance().initilize(LOG_FILE_PATH); //Get logger instance Logger LOGGER = Logger.getLogger(Log4jConfigReloadExample.class); //Print the log messages and wait for log4j changes while(true) { //Debug level log message LOGGER.debug("A debug message !!"); //Info level log message LOGGER.info("A info message !!"); //Wait between log messages Thread.sleep(2000); } } } Output: [main] INFO com.howtodoinjava.demo.Log4jConfigReloadExample - A info message !! [main] INFO com.howtodoinjava.demo.Log4jConfigReloadExample - A info message !! [main] INFO com.howtodoinjava.demo.Log4jConfigReloadExample - A info message !! [main] INFO com.howtodoinjava.demo.Log4jConfigReloadExample - A info message !! [main] INFO com.howtodoinjava.demo.Log4jConfigReloadExample - A info message !! Log4j configuration file changed. Reloading logging levels !! [main] DEBUG com.howtodoinjava.demo.Log4jConfigReloadExample - A debug message !! [main] INFO com.howtodoinjava.demo.Log4jConfigReloadExample - A info message !! [main] DEBUG com.howtodoinjava.demo.Log4jConfigReloadExample - A debug message !! [main] INFO com.howtodoinjava.demo.Log4jConfigReloadExample - A info message !!
Sourcecode Download
Happy Learning !!
how to implement run time log mode change in log4j2
Can this be done with a log4j.properties file instead of the xml file?
Yes, In case of properties file use PropertyConfigurator.configure() methods, in place of DOMConfigurator.configure() method in above example.
How to use same for mutliple log files.
Hi Lokesh,
Excellent article. It was really helpful. Won’t there be any performance impact because of one thread (Log4jChangeWatcherService in this case) running continuously ??
Not really. They are very light weight threads.
Suil…you are correct..I had tested with
PropertyConfigurator.configureAndWatch and is working lie a gem…:)…Thank you Lokesh and Sunil…
Why not DOMConfigurator.configureAndWatch()?
It’s for MS .net framework. Not sure, if it will work in java. Need to check. Can you please post a sample code here?
Here is the javadoc
http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/DOMConfigurator.html#configureAndWatch(java.lang.String, long)
Nice explanation & very usable
enable sharing via linkedin
No clue what you are asking for? Floating sidebar has share button for linkedIn. right?? Am i missing anything?