Slf4j Format String Example
In this example, we are going to see how String log messages can be constructed in SLF4J with parameter substitution.
1. Introduction
SLF4J is a facade or an abstraction layer over various logging frameworks. Also known as Simple logging Facade for Java is not actually a logging implementation, instead, it’s an abstraction layer. It allows you to use any logging library with it.
Now if you would ask why SLF4J, when we already have so many logging frameworks. The answer is, you can write your piece of pluggable code using SLF4J for logging without worrying about the underlying implementation. Now whatever the application which will use your pluggable piece, can use the logging implementation of it’s own choice.
While constructing logging messages, programmers often need to provide some piece of information to be able to trace the cause of the problem as well as to know general flow of information in erroneous as well as normal cases. SLF4J provides an easy way to construct these messages; in this example we will see how.
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.25
- Log4J 1.2.17
3. SLF4J Format String 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=slf4j-formatting -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 SLF4J and log4j. 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-formatting</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>slf4-formatting</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.25</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.25</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> </dependencies> </project>
3.3 Log4J Properties
SLF4J being an abstraction layer needs to have an underlying logging implementation. For this example we are using log4j. Create log4j.properties for the log4j 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
3.4 Test Class
We are going to use SLF4J’s style of parameter substitution for log messages formatting. Though these logging calls look simple but behind the scenes it uses org.slf4j.helpers.MessageFormatter
to format messages.
Slf4jSusbstitutionExample.java
package com.javacodegeeks.slf4.formatting; import java.lang.invoke.MethodHandles; import java.text.MessageFormat; import java.util.Calendar; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Substituting Parameters! * */ public class Slf4jSusbstitutionExample { private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); public static void main( String[] args ) { String user = "john"; String application = "gateway"; // Crafting a message without substitution. // Not a good idea as the String concatenation and evaluation will happen irrespective of whether // logging level is permissible or not to be logged. LOGGER.info("Bad experience for user " + user + " at time " + Calendar.getInstance().getTime()); // Substitution with one formatting anchor and one argument LOGGER.info("Bad experience for user {}", user); // If you happen to forget to provide a substituting object LOGGER.info("Bad experience for user {}"); // Substitution with two formatting anchors and two arguments LOGGER.info("Bad experience for user {} at time {}", user, Calendar.getInstance().getTime()); // Substitution with three formatting anchors and three arguments LOGGER.info("Bad experience for user {} at time {} while accessing {}", user, Calendar.getInstance().getTime(), application); // Escaping formatting anchor LOGGER.info("ERROR CODE \\{}; Bad experience for user {} at time {}", user, Calendar.getInstance().getTime()); // Formatting anchor with data inside; no problem LOGGER.info("ERROR CODE {22}; Bad experience for user {} at time {}", user, Calendar.getInstance().getTime()); // Crafting a message with Java's own MessageFormatter. // Not a good idea as per SLF4J's documentation. // 1. SLF4J's implementation is 10 times faster than that of MessageFormat. // 2. Moreover to make sure that the evaluation happens only if that particular logging // level is allowed, you need to do a check. if(LOGGER.isInfoEnabled()) { String message = MessageFormat.format("Bad experience for user {0} at time {1} while accessing {2}", user, Calendar.getInstance().getTime(), application); LOGGER.info(message); } } }
Output
2017-04-20 20:25:42 INFO Slf4jSusbstitutionExample - Bad experience for user john at time Thu Apr 20 20:25:42 IST 2017 2017-04-20 20:25:42 INFO Slf4jSusbstitutionExample - Bad experience for user john 2017-04-20 20:25:42 INFO Slf4jSusbstitutionExample - Bad experience for user {} 2017-04-20 20:25:42 INFO Slf4jSusbstitutionExample - Bad experience for user john at time Thu Apr 20 20:25:42 IST 2017 2017-04-20 20:25:42 INFO Slf4jSusbstitutionExample - Bad experience for user john at time Thu Apr 20 20:25:42 IST 2017 while accessing gateway 2017-04-20 20:25:42 INFO Slf4jSusbstitutionExample - ERROR CODE {}; Bad experience for user john at time Thu Apr 20 20:25:42 IST 2017 2017-04-20 20:25:42 INFO Slf4jSusbstitutionExample - ERROR CODE {22}; Bad experience for user john at time Thu Apr 20 20:25:42 IST 2017 2017-04-20 20:25:42 INFO Slf4jSusbstitutionExample - Bad experience for user john at time 4/20/17 8:25 PM while accessing gateway
As per SLF4J’s documentation of org.slf4j.helpers.MessageFormatter
, following points are worth noting:
Formats messages according to very simple substitution rules. Substitutions can be made 1, 2 or more arguments.
- The {} pair is called the formatting anchor. It serves to designate the location where arguments need to be substituted within the message pattern.
- If for whatever reason you need to place the string “{}” in the message without its formatting anchor meaning, then you need to escape the ‘{‘ character with ‘\’, that is the backslash character. Only the ‘{‘ character should be escaped. There is no need to escape the ‘}’ character.
- The formatting conventions are different than those of
MessageFormat
which ships with the Java platform. This is justified by the fact that SLF4J’s implementation is 10 times faster than that ofMessageFormat
. This local performance difference is both measurable and significant in the larger context of the complete logging processing chain.
4. Summary
SLF4J’s substitution parameters while constructing log messages is suggested over String concatenation for the following two reasons:
- No explicit check needed for log level
- Performance improvement – If the log message is not supposed to be logged then the message will not simply be constructed.
NOTE:
- SLF4J provides a mechanism for parameters substitution not C style parameters formatting. Formatting parameters lives with the underlying logging implementation (like the one PatternLayout provided by log4j).
- Markers are beyond the scope of this example. Also, SLF4J doesn’t provide any implementation for markers and leaves that part with the logging frameworks.
5. References
- https://www.slf4j.org/faq.html#yet_another_facade
- https://examples.javacodegeeks.com/enterprise-java/slf4j/slf4j-configuration-file-example/
- https://www.slf4j.org/api/org/slf4j/helpers/MessageFormatter.html