SLF4J

Slf4j Spring Boot Example

In this example, we are going to learn how to use SLF4J in Spring projects with an example of Spring Boot application.

1. Introduction

The Simple Logging Facade for Java (SLF4J) serves as a simple facade or abstraction for various logging frameworks (e.g. java.util.logging, logback, log4j) allowing the end user to plug in the desired logging framework at deployment time.

SLF4J facilitates your code to make logging calls without binding to a specific logging implementation. This comes real handy for libraries where it is not desired to hard bind the API to a particular logging framework as the program where it may be used might be using another logging implementation.

To get started with SLF4J, read this tutorial.

SLF4J has now become a logging abstraction of choice and probably fast ruling out Commons logging due to it’s advantages (read here for a head-on comparison).

Spring (what started as a Java enterprise application framework) has evolved into an ecosystem now with framework for enterprise applications, transaction management, web services, security, mobile development etc. Let’s see in this example what Spring has to offer when it comes to logging framework support.

2. Technologies Used

For this example, we use the following tools on a Windows 10 platform:

  • Eclipse IDE
  • Apache Maven
  • JDK 1.8
  • Slf4j 1.7.22
  • Spring Boot 1.5.3

3. Slf4j Spring Boot Example

3.1 Create a Maven Project

We will create a bare minimum Maven project. Once you have the Maven installed and running on your machine, issue the following command from the command line.

mvn archetype:generate -DgroupId=com.javacodegeeks -DartifactId=slf4-spring-boot -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

In this example, Maven will manage dependencies for us and we don’t need to download any jars explicitly.

3.2 Add Maven Dependencies

Add the required dependencies for just Spring Boot. Your pom.xml should look like this.

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.javacodegeeks</groupId>
	<artifactId>slf4-spring-boot</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>slf4-spring-boot</name>
	<url>http://maven.apache.org</url>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
			<version>1.5.3.RELEASE</version>
		</dependency>
	</dependencies>
</project>

With just the single dependency, spring boot will download the logging dependency (spring-boot-starter-logging)  as well among other core dependencies. If you open pom.xml and go to the “Dependency Hierarchy” tab, it looks like this:

Dependency Hierarchy
Figure 1: Dependency Hierarchy

Note the jar files spring-boot-starter-logging brings along in the figure above.  jcl-over-slf4j, jul-to-slf4j and log4j-over-slf4j are the bridging modules which redirect calls made to log4j, JCL and java.util.logging APIs to behave as if they were made to the SLF4J API instead. logback-classic along with logback-core enables logback as the default logging implementation for Spring Boot.  This may have to do with the fact that logback is the native implementation of SLF4J; using SLF4J in conjunction with logback involves strictly zero memory and computational overhead.

3.3 Logback Configuration

Create logback.xml and put the basic configuration settings. If we don’t create this file, even then logback will configure itself automatically using the BasicConfigurator which will cause logging output to be directed to the console.

logback.xml

<configuration>

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <!-- encoders are assigned the type
         ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
    <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>

3.4 Test Class

Slf4jSpringBootApplication.java

package com.javacodegeeks;

import java.lang.invoke.MethodHandles;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.impl.StaticLoggerBinder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import com.javacodegeeks.service.HelloWorldService;

@SpringBootApplication
public class Slf4jSpringBootApplication implements CommandLineRunner {

	// SLF4J's logging instance for this class
	// We could have used LoggerFactory.getLogger(Slf4jSpringBootApplication.class) as well
	private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
	
	// This is what SLF4J uses to bind to a specific logging implementation
	final StaticLoggerBinder binder = StaticLoggerBinder.getSingleton();
	
	@Autowired
	private HelloWorldService helloWorldService;

	public void run(String... args) {
		System.out.println(binder.getLoggerFactory());
		System.out.println(binder.getLoggerFactoryClassStr());
		
		LOGGER.debug(this.helloWorldService.getHelloMessage());
		if (args.length > 0 && args[0].equals("exitcode")) {
			LOGGER.error("Exit Code encountered", new ExitException());
		}
	}

	public static void main(String[] args) throws Exception {
		SpringApplication.run(Slf4jSpringBootApplication.class, args);
	}
}

Since the logging level is set to debug, it’s going to output a lot of Spring messages. Our logging specific calls is shown below.

Output Snippet

ch.qos.logback.classic.LoggerContext[default]
ch.qos.logback.classic.util.ContextSelectorStaticBinder
19:48:58.844 [main] INFO  c.j.service.HelloWorldService - Name registered World
19:48:58.846 [main] DEBUG c.j.Slf4jSpringBootApplication - Hello World
19:48:58.848 [main] ERROR c.j.Slf4jSpringBootApplication - Exit Code encountered
java.lang.Exception: null
	at com.javacodegeeks.Slf4jSpringBootApplication.run(Slf4jSpringBootApplication.java:34)
	at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:776)
	at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:760)
	at org.springframework.boot.SpringApplication.afterRefresh(SpringApplication.java:747)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:315)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1162)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1151)
	at com.javacodegeeks.Slf4jSpringBootApplication.main(Slf4jSpringBootApplication.java:39)

3.5 Configuring With Log4J

You can use spring-boot-starter-log4j2 in place of spring-boot-starter-logging so that log4j2 becomes the default dependency for your Spring Boot app. In this case our pom.xml will look like this.

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.javacodegeeks</groupId>
	<artifactId>slf4-spring-boot</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>slf4-spring-boot</name>
	<url>http://maven.apache.org</url>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
			<version>1.5.3.RELEASE</version>
			<exclusions>
				<exclusion>
					<groupId>org.springframework.boot</groupId>
					<artifactId>spring-boot-starter-logging</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-log4j2</artifactId>
			<version>1.5.3.RELEASE</version>
		</dependency>
	</dependencies>
</project>

Create log4j.properties for the logging configuration.

log4j.properties

# Root logger option
log4j.rootLogger=INFO, stdout

# 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} - %m%n

Let’s re-run Slf4jSpringBootApplication.java to see the output.

Output Snippet

org.apache.logging.slf4j.Log4jLoggerFactory@38b27cdc
org.apache.logging.slf4j.Log4jLoggerFactory
2017-04-29 20:28:02.924  INFO 14356 --- [           main] c.j.s.HelloWorldService                  : Name registered World
2017-04-29 20:28:02.925 ERROR 14356 --- [           main] c.j.Slf4jSpringBootApplication           : Exit Code encountered

java.lang.Exception: null
	at com.javacodegeeks.Slf4jSpringBootApplication.run(Slf4jSpringBootApplication.java:34) [classes/:?]
	at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:776) [spring-boot-1.5.3.RELEASE.jar:1.5.3.RELEASE]
	at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:760) [spring-boot-1.5.3.RELEASE.jar:1.5.3.RELEASE]
	at org.springframework.boot.SpringApplication.afterRefresh(SpringApplication.java:747) [spring-boot-1.5.3.RELEASE.jar:1.5.3.RELEASE]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) [spring-boot-1.5.3.RELEASE.jar:1.5.3.RELEASE]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1162) [spring-boot-1.5.3.RELEASE.jar:1.5.3.RELEASE]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1151) [spring-boot-1.5.3.RELEASE.jar:1.5.3.RELEASE]
	at com.javacodegeeks.Slf4jSpringBootApplication.main(Slf4jSpringBootApplication.java:39) [classes/:?]

Note in the above output, the SLF4J binding has switched to log4j2.

4. Summary

Spring Boot provides out of the box support for SLF4J and Logback is the default logging implementation for Spring Boot.

Default logging implementation can be changed by excluding corresponding bridging modules (jcl-over-slf4j, jult-to-slf4j and log4j-over-slf4j) as well as logback-classic and then add required logging library with the corresponding slf4j bridge.

5. References

  1. https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#overview-logging-slf4j
  2. https://projects.spring.io/spring-boot/
  3. https://docs.spring.io/spring-boot/docs/current/reference/html/howto-logging.html
  4. https://www.slf4j.org/legacy.html

6. Download the Eclipse Project

You can download the full source codes of this example here: slf4j-log4j-spring-boot & slf4j-spring-boot

Mayank Gupta

Senior JEE developer with experience in large scale IT projects, especially in the telecommunications and financial services sectors. Mayank has been designing and building J2EE applications since 2007. Fascinated by all forms of software development; keen to explore upcoming areas of technology like AI, machine learning, blockchain development and AR. Lover of gadgets, apps, technology and gaming.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button