{"id":21317,"date":"2015-07-01T16:05:41","date_gmt":"2015-07-01T10:35:41","guid":{"rendered":"http:\/\/www.tothenew.com\/blog\/?p=21317"},"modified":"2015-07-24T17:01:41","modified_gmt":"2015-07-24T11:31:41","slug":"externalize-and-reload-grails-log4j-configuration","status":"publish","type":"post","link":"https:\/\/www.tothenew.com\/blog\/externalize-and-reload-grails-log4j-configuration\/","title":{"rendered":"Externalize and reload grails Log4j configuration"},"content":{"rendered":"<p style=\"text-align: left;\">In <a title=\"Grails Version Upgrade Services\" href=\"http:\/\/www.tothenew.com\/grails-application-development\">Grails 2.x and in some earlier versions<\/a>, the log4j configuration resides in <code>grails-app\/conf\/Config.groovy<\/code>. We can modify log4j closure here to add new categories and tweak the log levels.<\/p>\n<p style=\"text-align: left;\">In development environemnt any changes to log4j closure are loaded automatically without bouncing the application, but not when the application is running from a war.<\/p>\n<p style=\"text-align: left;\">Our requirement is to have the ability to change log levels and add new categories at runtime.<\/p>\n<p style=\"text-align: left;\">Depending upon whether we want to use closure based or xml based log4j configuration, we can choose any of the below approaches.<\/p>\n<h3 style=\"text-align: left;\">1. <span style=\"text-decoration: underline;\">Modify web.xml<\/span><\/h3>\n<p style=\"text-align: left;\">While creating a war, grails generates web.xml for your application using template. To override the default web.xml, we need to override the template. Run <code>grails install-templates<\/code> in order to do that. Add two new context-param in <code>src\/templates\/war\/web.xml<\/code>.<\/p>\n<ol style=\"text-align: left;\">\n<li>log4jConfigLocation &#8211; holds the location of the log4 configuration file<\/li>\n<li>log4jRefreshInterval &#8211; defines the refresh interval after which the log4j configuration should be updated<\/li>\n<\/ol>\n<p style=\"text-align: left;\">We are going to use the <code>Log4jConfigListener<\/code> class of spring library instead of Grails log4jConfigListener as Grail\u2019s Log4jConfigListener expects the configuration inside the config.groovy file. Spring\u2019s Log4jConfigListener supports location and interval.<\/p>\n<p>[code lang=&#8221;xml&#8221;]<br \/>\n\t&lt;!&#8211; log4j parameters &#8211;&gt;<br \/>\n    &lt;context-param&gt;<br \/>\n      &lt;param-name&gt;log4jConfigLocation&lt;\/param-name&gt;<br \/>\n      &lt;param-value&gt;classpath:log4j.xml&lt;\/param-value&gt;<br \/>\n    &lt;\/context-param&gt;<\/p>\n<p>    &lt;context-param&gt;<br \/>\n      &lt;param-name&gt;log4jRefreshInterval&lt;\/param-name&gt;<br \/>\n      &lt;param-value&gt;10000&lt;\/param-value&gt;<br \/>\n    &lt;\/context-param&gt;<\/p>\n<p>    &lt;!&#8211; Register Spring&#8217;s Log4jConfigListener&#8211;&gt;<br \/>\n    &lt;listener&gt;<br \/>\n        &lt;listener-class&gt;org.springframework.web.util.Log4jConfigListener&lt;\/listener-class&gt;<br \/>\n    &lt;\/listener&gt;<br \/>\n[\/code]<\/p>\n<h3 style=\"text-align: left;\">2. <span style=\"text-decoration: underline;\">Using Bean Injection<\/span><\/h3>\n<p>[code lang=&#8221;groovy&#8221;]<br \/>\nimport org.springframework.beans.factory.config.MethodInvokingFactoryBean<\/p>\n<p>beans = {<br \/>\n\tlog4jConfigurer(MethodInvokingFactoryBean) {<br \/>\n\t\ttargetClass = &quot;org.springframework.util.Log4jConfigurer&quot;<br \/>\n\t\ttargetMethod = &quot;initLogging&quot;<br \/>\n\t\targuments = [&quot;classpath:log4j.xml&quot;, 10000]\/\/specify config location and refresh interval<br \/>\n\t}<br \/>\n}<br \/>\n[\/code]<\/p>\n<h3 style=\"text-align: left;\">3. <span style=\"text-decoration: underline;\">Custom approach<\/span><\/h3>\n<p style=\"text-align: left;\">In approach 1 &amp; 2, we can use .xml or .properties file. But if you are a grails lover and would like to use the same closure based syntax to define loggers then we can do this. Benefit of this approach is that it can load configuration from multiple files.<\/p>\n<p style=\"text-align: left;\">Specify external configuration files in <code>grails.config.locations<\/code> closure.<\/p>\n<p>[code lang=&#8221;groovy&#8221;]<br \/>\ngrails.config.locations = [<br \/>\n\t\tDataSourceConfig,<br \/>\n\t\tCommonConfig,<br \/>\n\t\tRabbitMQConfig,<br \/>\n\t\t&quot;classpath:CommonLoggerConfig.groovy&quot;<br \/>\n\t\t&quot;file:${userHome}\/.grailsConfigurations\/${appName}\/LoggerConfig.groovy&quot;<br \/>\n]<br \/>\n[\/code]<\/p>\n<p style=\"text-align: left;\">Now we can write a job that will periodically check for the changes in config files and will reload log4j configuration, if there are any. It will first update the configuration object and then will re-configure the log4j.<\/p>\n<p style=\"text-align: left;\">Here we are using the <code>quartz<\/code> api to do that. Create a new job ConfigurationReloaderJob.<\/p>\n<p>[code lang=&#8221;groovy&#8221;]<br \/>\nimport grails.util.Environment<br \/>\nimport grails.util.Holders<br \/>\nimport org.codehaus.groovy.grails.plugins.log4j.Log4jConfig<\/p>\n<p>class ConfigurationReloaderJob {<br \/>\n\tstatic triggers = {<br \/>\n\t\t\/\/Start the job 5 minutes after the application starts up and execute after every 1 minute<br \/>\n\t\tsimple name: &quot;ConfigurationReloaderJob&quot;, startDelay: 5 * 60 * 1000l, repeatInterval: 1 * 60 * 1000l<br \/>\n\t}<\/p>\n<p>\t\/\/to store the hash of external config files<br \/>\n\tprivate static Map&lt;String, Integer&gt; fileHashMap = [:]<\/p>\n<p>\tdef execute() {<br \/>\n\t\tConfigObject config = Holders.config<br \/>\n\t\tList locations = config.grails.config.locations<\/p>\n<p>\t\tint hashCode<br \/>\n\t\tString text<\/p>\n<p>\t\tlocations.findAll {<br \/>\n\t\t\t\/\/find all the external file locations(contains classpath: or file: in their name)<br \/>\n\t\t\treturn (it instanceof String || it instanceof GString) &amp;&amp; (it.toString().contains(&quot;classpath:&quot;) || it.toString().contains(&quot;file:&quot;))<br \/>\n\t\t}.each {<br \/>\n\t\t\tString filePath -&gt;<br \/>\n\t\t\t\ttry {<br \/>\n\t\t\t\t\t\/\/read contents of file<br \/>\n\t\t\t\t\ttext = Holders.applicationContext.getResource(filePath)?.file?.text<br \/>\n\t\t\t\t} catch (FileNotFoundException ex) {<br \/>\n\t\t\t\t\t\/\/ignore the exception if file not found on specified path<br \/>\n\t\t\t\t\tlog.error(&quot;File not found: ${ex.message}&quot;)<br \/>\n\t\t\t\t}<\/p>\n<p>\t\t\t\t\/\/if file have data<br \/>\n\t\t\t\tif (text) {<br \/>\n\t\t\t\t\t\/\/calculate hashCode of text<br \/>\n\t\t\t\t\thashCode = text.hashCode()<br \/>\n\t\t\t\t\tif (!fileHashMap.containsKey(filePath) || fileHashMap.get(filePath) != hashCode) {<br \/>\n\t\t\t\t\t\t\/\/Merger existing config with the text of file<br \/>\n\t\t\t\t\t\tconfig = config.merge(new ConfigSlurper(Environment.current.name).parse(text))<\/p>\n<p>\t\t\t\t\t\t\/\/reconfigure log4j<br \/>\n\t\t\t\t\t\tLog4jConfig.initialize(config)<\/p>\n<p>\t\t\t\t\t\tfileHashMap.put(filePath, hashCode)<br \/>\n\t\t\t\t\t\tlog.debug(&quot;Log configuration updated successfully.&quot;)<br \/>\n\t\t\t\t\t}<br \/>\n\t\t\t\t}<br \/>\n\t\t}<br \/>\n\t}<br \/>\n}<br \/>\n[\/code]<\/p>\n<h4 style=\"text-align: left; text-decoration: underline;\">Hooking into Grails Events<\/h4>\n<p style=\"text-align: left;\">As we are specifying that the configurations will be loaded from the classpath or system path, we will be hooking into the Grails events to make sure that the file is in the classpath. Create a file under scripts\/_Events.groovy and paste the code given below.<\/p>\n<p>[code lang=&#8221;groovy&#8221;]<br \/>\n\/\/Hook into war creation event<br \/>\neventCreateWarStart = { warName, stagingDir -&gt;<br \/>\n  ant.copy(todir: &quot;${stagingDir}\/WEB-INF\/classes&quot;) {<br \/>\n    fileset(file: &quot;${basedir}\/log4j.properties&quot;)<br \/>\n  }<br \/>\n}<\/p>\n<p>\/\/Hook into run-app event<br \/>\neventRunAppStart = {<br \/>\n  ant.copy(todir:&quot;${basedir}\/target\/classes&quot;) {<br \/>\n    fileset(file: &quot;${basedir}\/log4j.properties&quot;)<br \/>\n  }<br \/>\n}<br \/>\n[\/code]<\/p>\n<p style=\"text-align: left;\">You can modify the above code to work with multiple files or to copy files at file system path as specified in <code>grails.config.locations<\/code>.<\/p>\n<h4 style=\"text-align: left; text-decoration: underline;\">Changes to config.groovy<\/h4>\n<p style=\"text-align: left;\">In <code>grails-app\/conf\/config.groovy<\/code>, remove the log4j closure at the end of the file.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In Grails 2.x and in some earlier versions, the log4j configuration resides in grails-app\/conf\/Config.groovy. We can modify log4j closure here to add new categories and tweak the log levels. In development environemnt any changes to log4j closure are loaded automatically without bouncing the application, but not when the application is running from a war. Our [&hellip;]<\/p>\n","protected":false},"author":182,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"iawp_total_views":30},"categories":[7,1],"tags":[1886,207,4840,471,1889],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/21317"}],"collection":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/users\/182"}],"replies":[{"embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/comments?post=21317"}],"version-history":[{"count":0,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/21317\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/media?parent=21317"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/categories?post=21317"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/tags?post=21317"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}