原文: 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 !!
祝您学习愉快!