简体   繁体   中英

Dynamically reload log4j.properties for grails application running in tomcat

I have a grails application, deployed to tomcat, that is using log4j. I would like to be able to update /webapps/WEB-INF/classes/log4j.properties in tomcat, and then have the application dynamically pickup the changes without requiring a restart. I haven't had any luck finding a good way to do this. What I have figured out is:

I can retrieve a file as a stream:

Thread.currentThread().getContextClassLoader().getResourceAsStream("log4j.properties")

Unfortunately this is the class loaded during startup, and I'm not sure if/how I can force it to update from the actual file. If that worked I could read each property and use:

Logger.rootLogger.loggerRepository.getLogger(<key>).level = <new log level>

I've seen things about LogManager.resetConfiguration() but that doesn't seem to help either.

Also, this is how I setup log4j in resources.groovy

beans = {
    // Setting up external configuration for log4j
    log4jConfigurer(org.springframework.beans.factory.config.MethodInvokingFactoryBean) {
        targetClass = "org.springframework.util.Log4jConfigurer"
        targetMethod = "initLogging"
        arguments = ["classpath:log4j.properties"]
    }
}

I'm not interested in using the configureAndWatch approach as I've read about its vulnerabilities.

I see that there are XML and JSON properties you can use for log4j, and I'm just using a plain *.properties file, and I'm not sure if that is part of the problem.

Any tips would be greatly appreciated, thanks in advance!

The following is what I did to get around this issue, it required setUseCaches(false), to make sure that cached versions of a file (from the classloader) were not used.

def fileInputStream = null
// The method below is used to ensure that the file is read from disk every time, rather than using the previously loaded file from the ClassLoader
def classLoader = Thread.currentThread().getContextClassLoader()
def resource = classLoader.getResource("log4j.properties")
def connection = resource.openConnection()
connection.setUseCaches(false)
// Warning: do not read or log the fileInputStream, because it will close the connection
fileInputStream = (FileInputStream)connection.getInputStream().in
// Use the Properties object to prevent errors on comments (#)
def props = new Properties()
props.load(fileInputStream)
def config = new ConfigSlurper().parse(props)

Then I can reference logger properties using:

config.log4j.logger.each {
    Logger.rootLogger.loggerRepository.getLogger(it.key).level = Level.toLevel(it.value)
}

You can also make checks to see what the existing log levels are and provide output to state which logging has actually changed.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM