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

> 原文： [https://howtodoinjava.com/log4j/how-to-reload-log4j-levels-on-runtime/](https://howtodoinjava.com/log4j/how-to-reload-log4j-levels-on-runtime/)

过多的日志记录是导致应用性能下降的常见原因。 它是[**最佳实践**](//howtodoinjava.com/category/best-practices/ "java best practices")之一，可确保 Java EE 应用实现中的正确日志记录。 但是，请注意在生产环境中启用的日志记录级别。 过多的日志记录将触发服务器上的高 IO，并增加 CPU 使用率。 对于使用较旧硬件的较旧环境或处理大量并发卷的环境而言，这尤其可能成为问题。

```java
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`**](https://docs.oracle.com/javase/7/docs/api/java/nio/file/WatchService.html "WatchService")完成此操作。

## 步骤 1）实现一个`WatchService`，它将监听给定 log4j 文件中的更改

下面的给定代码初始化了一个线程，该线程连续监视给定 log4j 文件（`StandardWatchEventKinds.ENTRY_MODIFY`）中的修改。 一旦观察到文件更改，就会调用`configurationChanged()`方法，并使用 `DOMConfigurator.configure()`方法再次重新加载 log4j 配置。

**`Log4jChangeWatcherService.java`**

```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`**

```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`**

```java
<?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`**

```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 !!

```

**祝您学习愉快！**