Logging in Groovy with replaceable Logger-implementations

With version 1.8 a great feature got into the release of Groovy: the groovy.util.logging.Slf4j-annotation. With that annotation you can create the logging facade which can be used then at runtime with different logging implementations. In this example I will use „simple logging“, „log4j“ and „logback“ as implementations.

The project structure:

├── libs-log4j
│   ├── log4j-1.2.17.jar
│   ├── log4j.properties
│   ├── slf4j-api-1.7.22.jar
│   └── slf4j-log4j12-1.7.22.jar
├── libs-logback
│   ├── logback-classic-1.2.1.jar
│   ├── logback-core-1.2.1.jar
│   ├── logback.xml
│   └── slf4j-api-1.7.22.jar
├── libs-simplelogging
│   ├── slf4j-api-1.7.22.jar
│   └── slf4j-simple-1.7.22.jar
└── example.groovy

The example file where a Groovy class gets annotated with Slf4j

import groovy.util.logging.Slf4j

// Use annotation to inject log field into the class.
@Slf4j
class HelloWorldSlf4j {
    def execute() {
        log.debug 'say sth with debug words'
        log.info 'say sth with info words.'
    }
}

def helloWorld = new HelloWorldSlf4j()
helloWorld.execute()

This annotation provides the logging faced in form of the field „log“ that then can be simply used as injected field instead of defining your own Logger. That makes the usage of logging much cleaner while the implementation can be replaced easily by just providing different logging implementations:


(source: slf4j.org)

First we use the simple logging library by including the jar (next to the slf4j-jar) into the classpath:

christoph@hephaistos:~/groovy-examples$ groovy -cp "libs-simplelogging/*" example.groovy 
[main] INFO HelloWorldSlf4j - say sth with info words.

The next implementation is log4j where we provide next to the jars a log4j.properties

christoph@hephaistos:~/groovy-examples$ groovy -cp "libs-log4j/*" -Dlog4j.configuration=libs-log4j/log4j.properties example.groovy 
2017-02-22 19:57:06 DEBUG HelloWorldSlf4j:? - say sth with debug words
2017-02-22 19:57:06 INFO  HelloWorldSlf4j:? - say sth with info words.
# Root logger option
log4j.rootLogger=DEBUG, file, stdout

# Direct log messages to a log file
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=test.log
log4j.appender.file.MaxFileSize=10MB
log4j.appender.file.MaxBackupIndex=10
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n

# Direct log messages to stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n

And last but not least: logback

christoph@hephaistos:~/groovy-examples$ groovy -cp "libs-logback/*" example.groovy 19:58:10.820 [main] DEBUG HelloWorldSlf4j - say sth with debug words
19:58:10.823 [main] INFO HelloWorldSlf4j - say sth with info words.
<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="DEBUG">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

All together this shows how easily a logging implementation can be replaced if you rely on the built-in slf4j-facade 🙂

References