We would like to use Log4j2 as the log binding with grails 3.
From what I can figure out so far. We have many subordinate dependencies that use a variety of loggers, so we need to use the SLF4J API. Then, instead of letting grails / groovy / spring re-direct the SLF4J API to the Logback binding, we need to re-direct each to the Log4j2 binding.
Since grails 3 uses the Logback binding, I am planning to go through each dependency in the build.gradle, exclude the Logback binding, and include the Log4j2 binding. Will this work? Update: Yes
Do we also need to bridge the Log4j2 API to the SLF4j API? What dependency do we need for that? Update: See below.
Finally, I assume we need to ditch the grails 3 logback.groovy configuration and just put one of the log4j2 configs in src/main/resources. Update: Yes
I'll post updates as we figure this out, but I bet someone has done this before.
Update 2016-03-18:
This turned out to be very straight-forward. I did a './gradlew dependencies' on my grails 3 project to see which dependencies were pulling in the Logback binding/implementation (group: 'ch.qos.logback', module: 'logback-classic')
First, here's the default build.gradle generated via a 'grails create-app testit' command:
buildscript {
ext {
grailsVersion = project.grailsVersion
}
repositories {
mavenLocal()
maven { url "https://repo.grails.org/grails/core" }
}
dependencies {
classpath "org.grails:grails-gradle-plugin:$grailsVersion"
classpath "com.bertramlabs.plugins:asset-pipeline-gradle:2.5.0"
classpath "org.grails.plugins:hibernate4:5.0.2"
}
}
version "0.1"
group "testit"
apply plugin:"eclipse"
apply plugin:"idea"
apply plugin:"war"
apply plugin:"org.grails.grails-web"
apply plugin:"org.grails.grails-gsp"
apply plugin:"asset-pipeline"
ext {
grailsVersion = project.grailsVersion
gradleWrapperVersion = project.gradleWrapperVersion
}
repositories {
mavenLocal()
maven { url "https://repo.grails.org/grails/core" }
}
dependencyManagement {
imports {
mavenBom "org.grails:grails-bom:$grailsVersion"
}
applyMavenExclusions false
}
dependencies {
compile "org.springframework.boot:spring-boot-starter-logging"
compile "org.springframework.boot:spring-boot-autoconfigure"
compile "org.grails:grails-core"
compile "org.springframework.boot:spring-boot-starter-actuator"
compile "org.springframework.boot:spring-boot-starter-tomcat"
compile "org.grails:grails-dependencies"
compile "org.grails:grails-web-boot"
compile "org.grails.plugins:cache"
compile "org.grails.plugins:scaffolding"
compile "org.grails.plugins:hibernate4"
compile "org.hibernate:hibernate-ehcache"
console "org.grails:grails-console"
profile "org.grails.profiles:web:3.1.4"
runtime "org.grails.plugins:asset-pipeline"
runtime "com.h2database:h2"
testCompile "org.grails:grails-plugin-testing"
testCompile "org.grails.plugins:geb"
testRuntime "org.seleniumhq.selenium:selenium-htmlunit-driver:2.47.1"
testRuntime "net.sourceforge.htmlunit:htmlunit:2.18"
}
task wrapper(type: Wrapper) {
gradleVersion = gradleWrapperVersion
}
assets {
minifyJs = true
minifyCss = true
}
The dependency report showed that they were being pulled in by two dependencies:
compile "org.springframework.boot:spring-boot-starter-logging"
and
compile "org.springframework.boot:spring-boot-starter-actuator"
So, I only had to make a couple changes to the dependencies section of the build.gradle:
dependencies {
// commented out the original way using Logback
//compile "org.springframework.boot:spring-boot-starter-logging"
// added the new way using Log4j2, yes, spring makes it easy
compile "org.springframework.boot:spring-boot-starter-log4j2"
// changed spring-boot-autoconfigure so that it would not
// pull in the logback binding/implementation
compile ('org.springframework.boot:spring-boot-autoconfigure') {
exclude group: 'ch.qos.logback', module: 'logback-classic'
}
// and finally, added the log4j2 binding/implementation
compile "org.apache.logging.log4j:log4j-api:2.5"
compile "org.apache.logging.log4j:log4j-core:2.5"
// the rest is unchanged
compile "org.grails:grails-core"
compile "org.springframework.boot:spring-boot-starter-actuator"
compile "org.springframework.boot:spring-boot-starter-tomcat"
compile "org.grails:grails-dependencies"
compile "org.grails:grails-web-boot"
compile "org.grails.plugins:cache"
compile "org.grails.plugins:scaffolding"
compile "org.grails.plugins:hibernate4"
compile "org.hibernate:hibernate-ehcache"
console "org.grails:grails-console"
profile "org.grails.profiles:web:3.1.4"
runtime "org.grails.plugins:asset-pipeline"
runtime "com.h2database:h2"
testCompile "org.grails:grails-plugin-testing"
testCompile "org.grails.plugins:geb"
testRuntime "org.seleniumhq.selenium:selenium-htmlunit-driver:2.47.1"
testRuntime "net.sourceforge.htmlunit:htmlunit:2.18"
}
In the src/main/resources, we added a log4j2.xml.
In the groovy code, we used:
import org.apache.logging.log4j.Logger
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.ThreadContext
private static final Logger log = LogManager.getLogger(getClass())
log.info('Hello World')
We also put ThreadContext statements in the constructors of heavily used classes.
That was it. Now we're doing fast, async logging that doesn't lose any log messages upon config changes.
Log4j v2. 17.0 addresses CVE-2021-45105. It protects against infinite recursion in lookup evaluation.
Grails uses its common configuration mechanism to configure the underlying Log4j log system: simply add a log4j setting to the file grails-app/conf/Config.
Configuration: the root element of a log4j2 configuration file; the status attribute represents the level at which internal log4j events should be logged. Appenders: this element contains a list of appenders; in our example, an appender corresponding to the System console is defined.
Forgot to post something as an answer. Here's an answer you can vote for, but I put all the information about the solution in the comments above.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With