原文: https://howtodoinjava.com/log4j/how-to-reload-log4j-levels-on-runtime/
过多的日志记录是导致应用性能下降的常见原因。 它是最佳实践之一,可确保 Java EE 应用实现中的正确日志记录。 但是,请注意在生产环境中启用的日志记录级别。 过多的日志记录将触发服务器上的高 IO,并增加 CPU 使用率。 对于使用较旧硬件的较旧环境或处理大量并发卷的环境而言,这尤其可能成为问题。
A balanced approach is to implement a "reloadable logging level" facility to turn extra logging ON / OFFwhen required in your day to day production support.
让我们看看如何使用 Java 7 提供的WatchService完成此操作。
步骤 1)实现一个WatchService,它将监听给定 log4j 文件中的更改
下面的给定代码初始化了一个线程,该线程连续监视给定 log4j 文件(StandardWatchEventKinds.ENTRY_MODIFY)中的修改。 一旦观察到文件更改,就会调用configurationChanged()方法,并使用 DOMConfigurator.configure()方法再次重新加载 log4j 配置。
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 changedpublic 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 handlePath 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 triggeredconfigurationChanged(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)添加Log4jConfigurator,这是您的应用与 log4j 协作的接口
此类是一个工具类,它将 log4j 初始化代码和重载策略与应用代码分开。
Log4jConfigurator.java
package com.howtodoinjava.demo;public class Log4jConfigurator{//This ensures singleton instance of configuratorprivate 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 loggerspublic 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 threadLog4jChangeWatcherService listner = new Log4jChangeWatcherService(file);//Start the threadnew Thread(listner).start();}catch (Exception e){e.printStackTrace();}}}
3)测试 log4j 重载事件
为了测试代码,我记录了两个两个语句:一个调试级别和一个信息级别。 两个语句在循环 2 秒后都被记录。 我将在一些日志语句之后更改日志记录级别,并且应该重新加载 log4j。
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 serviceLog4jConfigurator.getInstance().initilize(LOG_FILE_PATH);//Get logger instanceLogger LOGGER = Logger.getLogger(Log4jConfigReloadExample.class);//Print the log messages and wait for log4j changeswhile(true){//Debug level log messageLOGGER.debug("A debug message !!");//Info level log messageLOGGER.info("A info message !!");//Wait between log messagesThread.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 !!
祝您学习愉快!
