Log4j – 在运行时重新加载日志记录级别

Log4j – 在运行时重新加载日志记录级别

原文: 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 / OFF 
when 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 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)添加Log4jConfigurator,这是您的应用与 log4j 协作的接口

此类是一个工具类,它将 log4j 初始化代码和重载策略与应用代码分开。

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)测试 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 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 !!

祝您学习愉快!