Logging is a crucial part of the observability of your Java applications. Java logs combined with JVM metrics and traces give full observability into the application behavior and are invaluable when troubleshooting. Together, they shorten the time needed to find the root cause and allows for quick and efficient resolutions of problems.
Choosing the right logging library for your Java application is one of the steps to enable logging. As we explained in our Java logging tutorial, there are various frameworks available and there will be new ones that will enable faster, more powerful, and less resource expensive logging. That’s why it’s a good practice to use a logging API in your code – an abstraction layer that hides the implementation details and allows compile time logging framework binding.
In this article, we’ll introduce you to SLF4J, an abstraction layer allowing you to change the logging library without the need of changing the code of your Java application. We’ll talk about its benefits and how to configure and use it with the logging framework of your choice.
What Is SLF4J
The SLF4J or the Simple Logging Facade for Java is an abstraction layer for various Java logging frameworks, like Log4j 2 or Logback. This allows for plugging different logging frameworks at deployment time without the need for code changes.
Why Should You Use It
The Simple Logging Facade for Java allows us to decouple the code that is used to create log events from the implementation that will be responsible for processing and storing them in a way we want. For example, in a file or in an external log centralization system like Sematext Logs. This kind of approach allows choosing the logging framework such as Log4j 2 or Loback during compilation just by using a different library that uses the SLF4J API.
SLF4J Configuration Example
To enable SLF4J in your project you need to include the slf4j-api library and logging framework of your choice. For the purpose of this blog post, we will also include the slf4j-simple library, so that we can show how the SLF4J API looks in an easy way without complicating it with additional logging framework configuration. The slf4j-simple results in the SLF4J facade printing all log messages with INFO level or above to be printed in the System.err. Because of that, our Gradle build.gradle file looks as follows (you can find the whole project on Sematext Github account):
dependencies { implementation 'org.slf4j:slf4j-api:1.7.30' implementation 'org.slf4j:slf4j-simple:1.7.30' }
The code that will generate our SLF4J log message looks as follows:
package com.sematext.blog.logging; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class SLF4J { private static Logger LOGGER = LoggerFactory.getLogger(SLF4J.class); public static void main(String[] args) { LOGGER.info("This is an info level log message!"); } }
We create the Logger instance by using the LoggerFactory class and its getLogger method and providing the name of the class. That way we bind the logger to the class name which gives us the context of the log message. Once we have that, we can use the Logger methods to create LogRecord on a given level.
The above code execution results in the following output:
[main] INFO com.sematext.blog.logging.SLF4J - This is an info level log message!
Using SLF4J With Logging Framework at the Deploy Time
When using SLF4J you probably won’t be using the slf4j-simple library and you’ll want to use it with a dedicated logging framework so that your logs can be put in the destination of your choice or even multiple destinations of choice.
For SLF4J to be able to work with a logging framework of your choice in addition to the slf4j-api library you need to choose the appropriate bindings dedicated to your logging framework.
Using Log4j 2
To use SLF4J with Log4j 2 you need to include the log4j-slf4j18-impl-SLF4J_VERSION.jar for SLF4J 1.8 and newer or log4j-slf4j-impl-SLF4J_VERSION.jar for SLF4J up to 1.8. For more information on Log4j 2 have a look at our Log4j 2 tutorial.
Using Logback
To use SLF4J with Logback you need to include the logback-classic-1.2.3.jar library. If you would like to learn more about Logback itself have a look at our Logback tutorial.
Using Log4j
To use SLFJ with the first version of Log4j the slf4j-log4j12-SLF4J_VERSION.jar will be needed. We got into details about how these work together in our Log4j tutorial.
Using JCL
To use SLF4J with Java Common Logging you will need to include the slf4j-jdk1.4-SLF4J_VERSION.jar library.
Additional Considerations
Starting with the 1.6 version of the SLF4J library, if no bindings are found on the classpath SLF4J API will start to discard all log messages silently. So please keep that in mind.
It is also a good practice for the libraries and embedded software to only include dependency to the slf4j-api library and nothing else. That way the binding will be chosen by the developer of the application that uses the library.
SLF4J Loggers
When creating log events in Java you will be mainly interacting with the Logger object. The Logger object exposes the necessary methods allowing us to create the log events. Obtaining an instance of the Logger is as simple as using the getLogger method of the LoggerFactory class and providing the name of the logger to it. For example:
package com.sematext.blog.logging; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class SLF4J { private static Logger LOGGER = LoggerFactory.getLogger(SLF4J.class); public static void main(String[] args) { LOGGER.info("This is an info level log message!"); } }
Usually the Logger instance will be associated with a given class and be created in the static context.
One we have the Logger instance we can use it to produce log events, for example:
LOGGER.info("This is an INFO level log message")
SLF4J Levels
A level or severity is connected to a log event. It tells how important a log event is. In most logging frameworks you will find a common set of log levels and SLF4J is not different. The following log levels can be used when choosing the SLF4J as your logging library abstraction:
- TRACE – log events with this level are the most fine-grained and are usually not needed unless you need to have the full visibility of what is happening in your application and inside the third-party libraries that you use. You can expect the TRACE logging level to be very verbose.
- DEBUG – less granular compared to the TRACE level, but still more than you will need in everyday use. The DEBUG log level should be used for information that may be needed for deeper diagnostics and troubleshooting.
- INFO – the standard log level indicating that something happened, application processed a request, etc. The information logged using the INFO log level should be purely informative and not looking into them on a regular basis shouldn’t result in missing any important information.
- WARN – the log level that indicates that something unexpected happened in the application. For example a problem, or a situation that might disturb one of the processes, but the whole application is still working.
- ERROR – the log level that should be used when the application hits an issue preventing one or more functionalities from properly functioning. The ERROR log level can be used when one of the payment systems is not available, but there is still the option to check out the basket in the e-commerce application or when your social media logging option is not working for some reason. You can also see the ERROR log level associated with exceptions.
If all these are new to you, check out our tutorial on log levels, where we explain in greater detail what they are and how to use them for efficient logging. Or, if you like videos, check out our short explainer video in how to understand log levels below:
[sematext_banner type=”logs”]
Mapped Diagnostic Context in SLF4J
SLF4J supports the Mapped Diagnostic Context which is a map where the application code provides key-value pairs which can be inserted by the logging framework in the log messages. If the underlying logging framework supports the MDC then SLF4J facade will pass the maintained key-value pairs to the used logging framework.
For example, we can include user with all the log messages:
package com.sematext.blog.logging; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.MDC; public class SLF4JMDC { private static Logger LOGGER = LoggerFactory.getLogger(SLF4JMDC.class); public static void main(String[] args) { MDC.put("user", "rafal.kuc@sematext.com"); LOGGER.info("This is an info level log message!"); LOGGER.warn("This is an WARN level log"); } }
Properly displaying the data associated with the log event using the Mapped Diagnostic Context is up to the logging framework.
SLF4J Markers
Markers are named objects that are used to enrich the data inside the single log event context. You can imagine marking a log event with the IMPORTANT marker, which will mean that the appender should for example store the event in a separate log file.
SLF4J allows you to use the log method of the Logger to provide an optional marker. For example, the following code initializes a marker and creates two log events – one without and one with a marker.
package com.sematext.blog.logging; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.Marker; import org.slf4j.MarkerFactory; public class MarkerExample { private static Logger LOGGER = LoggerFactory.getLogger(MarkerExample.class); private static final Marker IMPORTANT = MarkerFactory.getMarker("IMPORTANT"); public static void main(String[] args) { LOGGER.info("This is a log message that is not important!"); LOGGER.info(IMPORTANT, "This is a very important log message!"); } }
Marked log events can be treated specially – for example, written into a different file or sent to a different log centralization solution for alerting.
Parameterized Logging
The SLF4J API is well designed and allows not only for simple messages. The typical usage pattern will require parameterized log messages. Of course, SLF4J allows for that. Have a look at the following class (I omitted the import section for simplicity):
public class SLF4JParametrized { private static Logger LOGGER = LoggerFactory.getLogger(SLF4JParametrized.class); public static void main(String[] args) { int currentValue = 36; LOGGER.info("The parameter value in the log message is {}", currentValue); } }
The output of the above code execution looks as follows:
[main] INFO com.sematext.blog.logging.SLF4JParametrized - The parameter value in the log message is 36
As you can see the parameter placeholder was properly replaced and the log message contains the value. This allows for efficient log message building without the need of String concatenation or buffers and writers usage.
Java Logging Using Log Management Tools: How Sematext Logs Can Help
Now you know what SLF4J is and how to use this abstraction layer to set up logging with your favorite logging library for Java. But no matter what kind of logging library you will use, the volume of logs grows with the complexity of your environment.
You may get away with logging to a file when working with a small environment and use that for troubleshooting, but working with huge amounts of data quickly becomes unmanageable and you should end up using a log management solution to centralize and monitor your logs. You can either go for an in-house solution based on the open-source software or use one of the products available on the market like Sematext Logs.
A fully managed log monitoring and centralization solution will give you the freedom of not needing to manage yet another, usually quite complex, part of your infrastructure. It will allow you to manage a plethora of sources for your logs.
You may want to include logs like JVM garbage collection logs in your managed log solution. After turning them on for your applications and systems working on the JVM you will want to have them in a single place for correlation, analysis, and to help you tune the garbage collection in the JVM instances. Alert on logs, aggregate the data, save and re-run the queries, hook up your favorite incident management software.
If you’re interested in such a tool, give Sematext Logs a try. There’s a free 14-day trial for you to explore all its pro features. You can learn more about it and how it stacks up against similar solutions from our reviews of the best log management software, log analysis tools, and cloud logging services available out there.
Using log management and analysis solutions is one of the many tips you should apply to get the most out of your logs. We discussed this in greater detail – along with other advice – in our article about Java logging best practices
Conclusion
Logging and the information it carries are priceless not only when troubleshooting Java applications. In fact, logging is invaluable in troubleshooting in general, no matter if that is a Java application, HTTP server, router, or an internet of things device. Both the software and hardware give us a look into how they are working in the form of parametrized logs enriched with contextual information.
In this tutorial, we’ve started with setting up our application to use a logging abstraction so that we can use any of the SLF4J compatible libraries. We’ve learned what are the options when it comes to Java logging, how to add logging to your application, how to configure it. We learned about SLF4J and how it helps in safe proofing the application for future changes. We just have to remember that SLF4J on its own is not enough as it provides the abstraction layer and we need concrete implementation, such as Log4j2 or Java Commons Logging to be able to fully utilize the Java logging functionality.
I hope this article gave you an idea on how to prepare your Java application to be ready with a plethora of logging libraries and why you should start working with them right away if you haven’t already. And don’t forget, the key to efficient application logging is using a good log management tool. Try Sematext Logs to see how much easier things could be.
Good luck!