Log4jConfigListener动态改变记录级别及实现

本文介绍如何在不重启服务的情况下,动态地修改日志级别,利用Log4jConfigListener实现这一目标。通过在Spring Web中添加特定依赖,配置web.xml文件,并启动Log4jConfigListener监听日志配置文件的变化,使得日志级别能够实时更新。重点解析了Log4jConfigListener的工作原理,包括其内部如何通过线程扫描日志配置文件的变化,并触发相应更新,确保应用程序的日志输出级别可以在运行时动态调整。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

线上的系统优先级都设为INFO之上,如果修日日志级别,需要重启服务,对线上的影响比较大。如何做到动态地修改日志级别,而且不用重启服务?Log4jConfigListener就上场了

Log4jConfigListenerspring-web中,需要添加maven的依赖:

            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-web</artifactId>
                <version>${spring.version}</version>
            </dependency>

web.xml中配置

    <context-param>
        <param-name>log4jConfigLocation</param-name>
        <param-value>classpath:log4j.xml</param-value>
    </context-param>

    <context-param>
        <param-name>log4jRefreshInterval</param-name>
        <param-value>60000</param-value>
    </context-param>

        <listener>
        <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
    </listener>

想动态修改日志级别,只需要修改log4j.xml

那么,Log4jConfigListener做了什么,可以知道文件变化了并加以应用,难道是起了个线程来做的?

首先看下Log4jConfigListener

public class Log4jConfigListener implements ServletContextListener {

    public void contextInitialized(ServletContextEvent event) {
        Log4jWebConfigurer.initLogging(event.getServletContext());
    }

    public void contextDestroyed(ServletContextEvent event) {
        Log4jWebConfigurer.shutdownLogging(event.getServletContext());
    }

}

这里Log4jConfigListener使用了Log4jWebConfigure,让我们继续

public static void initLogging(ServletContext servletContext) {
        // Expose the web app root system property.
        if (exposeWebAppRoot(servletContext)) {
            WebUtils.setWebAppRootSystemProperty(servletContext);
        }

        // Only perform custom log4j initialization in case of a config file.
        String location = servletContext.getInitParameter(CONFIG_LOCATION_PARAM);
        if (location != null) {
            // Perform actual log4j initialization; else rely on log4j's default initialization.
            try {
                // Resolve system property placeholders before potentially
                // resolving a real path.
                location = SystemPropertyUtils.resolvePlaceholders(location);

                // Leave a URL (e.g. "classpath:" or "file:") as-is.
                if (!ResourceUtils.isUrl(location)) {
                    // Consider a plain file path as relative to the web
                    // application root directory.
                    location = WebUtils.getRealPath(servletContext, location);
                }

                // Write log message to server log.
                servletContext.log("Initializing log4j from [" + location + "]");

                // Check whether refresh interval was specified.
                String intervalString = servletContext.getInitParameter(REFRESH_INTERVAL_PARAM);
                if (intervalString != null) {
                    // Initialize with refresh interval, i.e. with log4j's watchdog thread,
                    // checking the file in the background.
                    try {
                        long refreshInterval = Long.parseLong(intervalString);
                        Log4jConfigurer.initLogging(location, refreshInterval);
                    }
                    catch (NumberFormatException ex) {
                        throw new IllegalArgumentException("Invalid 'log4jRefreshInterval' parameter: " + ex.getMessage());
                    }
                }
                else {
                    // Initialize without refresh check, i.e. without log4j's watchdog thread.
                    Log4jConfigurer.initLogging(location);
                }
            }
            catch (FileNotFoundException ex) {
                throw new IllegalArgumentException("Invalid 'log4jConfigLocation' parameter: " + ex.getMessage());
            }
        }
    }

这里有几行代码需要是重点,

String intervalString = servletContext.getInitParameter(REFRESH_INTERVAL_PARAM);
Log4jConfigurer.initLogging(location, refreshInterval);

Log4jConfigure.initLogging有干了啥呢?

public static void initLogging(String location, long refreshInterval) throws FileNotFoundException {
        String resolvedLocation = SystemPropertyUtils.resolvePlaceholders(location);
        File file = ResourceUtils.getFile(resolvedLocation);
        if (!file.exists()) {
            throw new FileNotFoundException("Log4j config file [" + resolvedLocation + "] not found");
        }
        if (resolvedLocation.toLowerCase().endsWith(XML_FILE_EXTENSION)) {
            DOMConfigurator.configureAndWatch(file.getAbsolutePath(), refreshInterval);
        }
        else {
            PropertyConfigurator.configureAndWatch(file.getAbsolutePath(), refreshInterval);
        }
    }
获取配置文件,根据log4配置文件的格式(xml,properties)方式进行加载xml,那么一定是在`DOMConfigurator.configureAndWatch` 或者`PropertyConfigurator.configureAndWatch`里面有个线程在做幕后工作,由于LZ采用的是XML格式的配置文件,那就看下`DOMConfigurator.configureAndWatch`,看看它到底怎么实现的吧。

static
public
void configureAndWatch(String configFilename, long delay) {
XMLWatchdog xdog = new XMLWatchdog(configFilename);
xdog.setDelay(delay);
xdog.start();
}

`XMLWatchdog`,这是个`WatchDog`

class XMLWatchdog extends FileWatchdog {

XMLWatchdog(String filename) {
super(filename);

}

/**
Call {@link DOMConfigurator#configure(String)} with the
filename to reconfigure log4j. */
public
void doOnChange() {
new DOMConfigurator().doConfigure(filename,
LogManager.getLoggerRepository());
}
}

FileWatchDog

public abstract class FileWatchdog extends Thread{
…….

abstract
protected
void doOnChange();

public void run() {
while(!interrupted) {
try {
Thread.sleep(delay);
} catch(InterruptedException e) {
// no interruption expected
}
checkAndConfigure();
}
}
protected void checkAndConfigure() {
……..

if(fileExists) {
  long l = file.lastModified(); // this can also throw a SecurityException
  if(l > lastModif) {           // however, if we reached this point this
lastModif = l;              // is very unlikely.
doOnChange();
warnedAlready = false;
  }
} else {
  if(!warnedAlready) {
LogLog.debug("["+filename+"] does not exist.");
warnedAlready = true;
  }
}

}
}
``
FileWatchDog有个抽象方法,doOnChange`,就是对文件变化后的响应,抽象方法的定义,为子类的扩展提供了可能。

我们看到,Log4jConfirgureListener也就是通过线程的方式扫描log4j.xml,当发现log4j的配置文件发生变化后就作出响应,从而做到了不重启应用修改日志的输出级别。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值