Grails – Multiple Log4j Configurations

This is a quick tip that I found very useful and – to my knowledge – is not part of the Grails documentation by now. So let’s spread the word :-)

In one of my Grails projects, we use external configuration files to apply customer specific settings. We have a custom ConfigurationLoader that is used to locate customer-specific configuration files based on the current environment (VM arguments, *.properties file and such). The configuration loader returns a list of additional configuration files which is handed over to grails.config.locations [0]

// ConfigurationLoader retrieves customConfigLocations, being an ArrayList<String>

grails.config.locations = customConfigLocations

So far so good. Lately we had the requirement to specify customer-specific Log4J log levels, as various components needed a more fine-grained logging level. In Grails versions prior to Grails 2.0 we could have only satisfied this requirement by completely overriding the log4j variable from Config.groovy in the custom configuration file.


// log4j configuration
log4j = {

    error  'org.codehaus.groovy.grails.web.servlet',  //  controllers
           'org.codehaus.groovy.grails.web.pages', //  GSP
           'org.codehaus.groovy.grails.web.sitemesh', //  layouts
           'org.codehaus.groovy.grails.web.mapping.filter', // URL mapping
           'org.codehaus.groovy.grails.web.mapping', // URL mapping
           'org.codehaus.groovy.grails.commons', // core / classloading
           'org.codehaus.groovy.grails.plugins', // plugins
           'org.codehaus.groovy.grails.orm.hibernate', // hibernate integration
           'org.springframework',
           'org.hibernate',
           'net.sf.ehcache.hibernate'
}

Luckily, Ian Roberts introduced a far more elegant way to extend the log4j configuration.

Using a map of closures

As of Grails 2.0, there is a way to extend the Log4J configuration rather than replacing it. The way to go is to use a log4j variable of type Map<String, Closure> instead of Closure.


// main configuration
log4j.main = {
   // ...
}

// external (customer-specific) configuration
log4j.external = {
  // common configuration
}

environments {
  development {
    log4j.env = {
      // environment-specific config
    }
  }
  production {
    log4j.env = {
      // environment-specific config
    }
  }
}

As you can see in the code listing above, log4j is now treated as map, rather than a single value object. As groovy.util.ConfigObject [1] extends LinkedHashMap, the log4j entries will be called in the defined order and the resulting configuration entries will be merged. With this patch, we were able to add customer-specific log levels to our external configuration files.

If you want to have a look at the corresponding patch introducing this feature, check out [2].

[0] Grails Documentation – Externalized configuration
[1] groovy.util.ConfigObject
[2] Jira GRAILS-7314

About andresteingress

I am working as Groovy & Grails freelance developer in Linz, Austria. I enjoy making music, working on GContracts, committing to the Groovy programming language and writing blog posts on various JVM related topics.
This entry was posted in grails, groovy. Bookmark the permalink.

3 Responses to Grails – Multiple Log4j Configurations

  1. Pingback: Blog bookmarks 03/28/2012 « My Diigo bookmarks

  2. Amad says:

    Andre,

    When using variable i.e. appName in log.env closure, its not getting resolved. I have

    def logDirectory = "${myHome ?: '.'}/logs"
    ..
    file: "${logDirectory}/${appName}.log",

    and i get

    log4j:ERROR Property missing when configuring log4j: myHome
    log4j:ERROR Property missing when configuring log4j: appName

  3. Eamonn says:

    Hey Andre,
    Nice post. I haven’t seen this anywhere else. Good work!
    Thanks,
    Eamonn

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>