So, Grails already sets up datasources backed by connection pools. Is there a way to leverage those for use with DBAppender
in Logback so that I don't have to create a separate parallel datasource/connection pool?
logback.groovy
is somewhat external to Grails, so it doesn't accept Spring autowiring, and other tricks like grails.util.Holders.findApplication()
don't appear to work.
Woof, this was a chore. Frankly, I'm a bit disillusioned with Logback. Logback creates it's own Spring ApplicationContext. So we have two separate contexts. Ugh. Also, it certainly doesn't help that the DSL that Logback uses for configuring Spring in Groovy is different than Grails'.
Since Logback gets fired up before Grails is completely started, we need to tell Logback to create some dummy appenders that will just store log messages until we fire up appenders from within Grails. We use the logback extension for Spring to do this.
build.gradle:
compile 'org.logback-extensions:logback-ext-spring:0.1.4'
logback.groovy:
import ch.qos.logback.ext.spring.DelegatingLogbackAppender
appender('DB', DelegatingLogbackAppender)
appender('STDOUT', DelegatingLogbackAppender)
resources.groovy:
import ch.qos.logback.ext.spring.ApplicationContextHolder
import ch.qos.logback.classic.encoder.PatternLayoutEncoder
import ch.qos.logback.classic.db.DBAppender
import ch.qos.logback.core.ConsoleAppender
import ch.qos.logback.core.db.DataSourceConnectionSource
import org.slf4j.LoggerFactory
beans = {
applicationContextHolder(ApplicationContextHolder)
loggerContext(LoggerFactory) { bean ->
bean.factoryMethod = "getILoggerFactory"
}
patternLayoutEncoder(PatternLayoutEncoder) { bean ->
bean.initMethod = 'start'
bean.destroyMethod = 'stop'
context = ref(loggerContext)
pattern = "%level %logger - %msg%n"
}
STDOUT(ConsoleAppender) { bean ->
bean.initMethod = 'start'
bean.destroyMethod = 'stop'
context = ref(loggerContext)
encoder = ref(patternLayoutEncoder)
}
connectionSource(DataSourceConnectionSource) { bean ->
bean.initMethod = 'start'
bean.destroyMethod = 'stop'
context = ref(loggerContext)
dataSource = ref(dataSource)
}
DB(DBAppender) { bean ->
bean.initMethod = 'start'
bean.destroyMethod = 'stop'
context = ref(loggerContext)
connectionSource = ref(connectionSource)
}
}
The ref(dataSource)
in the DataSourceConnectionSource
references the dataSource you have configured in application.yml
or application.groovy
.
Say you have multiple dataSources (or even one configured just for logback called dataSources.logging
. In that case the bean reference would be dataSource_logging
. The default dataSource in that case (called dataSources.dataSource
bean reference is just dataSource
. Took me a while to figure that one out.
All in all, i miss the days of configuring Log4j from within the Grails config file using the Grails DSL. I get that separating logging from Grails means one less thing for Graeme and the Grails team to deal with, but this was a major PITA for something that I thought would be common. ¯\_(ツ)_/¯