a thread named [FileWatchdog] but has failed to stop it.

异常原因:

01-Feb-2018 19:41:28.020 WARNING [localhost-startStop-2] org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesThreads The web application [xxx] appears to have started a thread named [FileWatchdog] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread:
 java.lang.Thread.sleep(Native Method)
 org.apache.log4j.helpers.FileWatchdog.run(FileWatchdog.java:104)
问题起因:

1.在web.xml里面配置了

	<context-param>
        <param-name>contextLog4jRefreshInterval</param-name>
        <param-value>60000</param-value>
	</context-param>
	<listener>
		<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
	</listener>
这样 org.springframework.web.util.Log4jConfigListener会在初始化log的时候进行初始化 org.apache.log4j.helpers.FileWatchdog的子类( org.apache.log4j.PropertyWatchdog[log4j配置源为.properties时]或者org.apache.log4j.xml.XMLWatchdog【log4j 配置源为.xml时】 优先为.xml)
/** Parameter specifying the refresh interval for checking the log4j config file */
public static final String REFRESH_INTERVAL_PARAM = "log4jRefreshInterval";
省略中......

 // Check whether refresh interval was specified.
String intervalString = servletContext.getInitParameter(REFRESH_INTERVAL_PARAM);
if (StringUtils.hasText(intervalString)) {
	// 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());
	}
}
在Log4jConfigurer.initLogging(location, refreshInterval);这个方法里面初始化监控狗:

	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);
		}
	}
  static
  public
  void configureAndWatch(String configFilename, long delay) {
    XMLWatchdog xdog = new XMLWatchdog(configFilename);
    xdog.setDelay(delay);
    xdog.start();
  }

或者:
  static
  public
  void configureAndWatch(String configFilename, long delay) {
    PropertyWatchdog pdog = new PropertyWatchdog(configFilename);
    pdog.setDelay(delay);
    pdog.start();
  }

造成这个问题的描述参考:http://www.bubuko.com/infodetail-1812902.html 

说一下解决方案吧:

1.自定义FileWatchdog的子类  编写一个stopFileWatchdog方法,在stop方法里面引用 钩子 interrupted 设为true 

这样就可以结束FileWatchdog自定义子类的run方法了

子类代码:

    private static ContextPropertyWatchdog contextPropertyWatchdog;
    private volatile boolean interrupted = false;

    private ContextPropertyWatchdog(String filename) {
        super(filename);
    }

    public static synchronized ContextPropertyWatchdog getContextPropertyWatchdog(String filename){
        if (null == contextPropertyWatchdog){
            contextPropertyWatchdog = new ContextPropertyWatchdog(filename);
        }
        return contextPropertyWatchdog;
    }

    public void doOnChange() {
        new PropertyConfigurator().doConfigure(filename,
                LogManager.getLoggerRepository());
    }

    public void stopWatchdog(){
        this.interrupted = true;
    }

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

调用:

监听器初始化方法中:  contextInitialized

    public void contextInitialized(ServletContextEvent servletContext) {
        String location = servletContext.getServletContext().getInitParameter("log4jConfigLocation");
        String intervalString = servletContext.getServletContext().getInitParameter("contextLog4jRefreshInterval");
        if (StringUtils.hasText(location) && StringUtils.hasText(intervalString)) {
            try {
                long refreshInterval = Long.parseLong(intervalString);
                String resolvedLocation = SystemPropertyUtils.resolvePlaceholders(location);
                File file = ResourceUtils.getFile(resolvedLocation);
                if (!file.exists()) {
                    throw new FileNotFoundException("Log4j config file [" + resolvedLocation + "] not found");
                } else {
                    stringName = file.getAbsolutePath();
                    ContextPropertyWatchdog pDog = ContextPropertyWatchdog.getContextPropertyWatchdog(stringName);
                    pDog.setDelay(refreshInterval);
                    pDog.start();
                }

            } catch (NumberFormatException var5) {
                throw new IllegalArgumentException("Invalid 'log4jRefreshInterval' parameter: " + var5.getMessage());
            } catch (FileNotFoundException var6) {
                throw new IllegalArgumentException("Invalid 'log4jConfigLocation' parameter: " + var6.getMessage());
            }
        }
    }
销毁方法中:contextDestroyed
if ( !"".equals(stringName) ){
                    ContextPropertyWatchdog contextPropertyWatchdog = ContextPropertyWatchdog.getContextPropertyWatchdog(stringName);
                    contextPropertyWatchdog.stopWatchdog();
                    contextPropertyWatchdog.join();
                    logger.info("=========================log4j 配置文件监控 退出====================");
                }
其中stringName是自己定义的一个常量:

private String stringName = "";

这个就可以完成这个功能了,但是这样有个缺点就是在tomcat - shutdown的时候会先等待监控狗sleep醒来之后在停止tomcat,时间会稍微多一点。

对了web.xml文件也进行了修改为:

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值