Friday, August 6, 2021

Why use SLF4J over Log4J for logging in Java? Example

Every Java programmers know that logging is critical for any Java application, especially server-side application, and many of them are already familiar with various logging libraries e.g. java.util.logging, Apache log4j, logback, but if you don't know about SLF4J, Simple logging facade for Java,  then it's time to learn and use SLF4J in your project. In this Java article, we will learn why using SLF4J is better than using log4j or java.util.logging. It’s been a long time, since I wrote 10 logging tips for Java programmer,I don’t remember anything I have writing about logging.

Anyway, let’s get back to the topic, on contrary to all those logging libraries, there is a major difference between them and SLF4J. SLF4J or Simple logging Facade for Java is not really a logging implementation, instead, it's an abstraction layer, which allows you to use any logging library in the back-end. 

If you are writing API or utility library, which can be used internally or externally, then you really don't want that any client, which uses your library, should also stick with your choice of logging library. 

Suppose, if a project is already using log4j, and you included a library say Apache Active MQ, which has dependency on logback, another logging library, then you need to include them as well, but if Apache Active MQ uses SL4J, you can continue with your logging library, without pain of adding and maintaining new logging framework. 

In short, SLF4J makes your code independent of any particular logging API, which is a good thing for public API developers. Though idea of abstracting logging library is not new and Apache commons logging is already using it, but now SLF4J is quickly becoming an standard for logging in Java world. 

Let's see couple of more reason to use SLF4J over log4j, logback or java.util.logging.




Prefer SLF4J over Log4J, logback and java.util.Logging

Prefer SL4j over Log4j in Java logging
As I said earlier, main motivation of using SLF4J in your code to write log statements is, to make your program, independent of any particular logging library, which might require different configuration than you already have, and introduce more maintenance headache. But apart from that, there is one more feature of SLF4J API, which convinced me to use SL4J over my long time favorite Log4j, that is know as place holder and represented as {} in code. Placeholder is pretty much same as %s in format() method of String, because it get substituted  by actual string supplied at runtime. 

This not only reduce lot of String concatenation in your code, but also cost of creating String object. This is true even if you might not need that, depending upon your log level in production environment e.g. String concatenation on DEBUG and INFO levels. 

Since Strings are immutable and they are created in the String pool, they consume heap memory and most of the time they are not needed e.g. a String used in the DEBUG statement is not needed when your application is running on ERROR level in production. 

By using SLF4J, you can defer String creation at runtime, which means only required Strings will be created. If you have been using log4j then you are already familiar with a workaround of putting debug statement inside if() condition, but SLF4J placeholders are much better than that.

This is how you would do in Log4j, but surely this is not fun and reduces the readability of code by adding unnecessary boiler-plate code.


if (logger.isDebugEnabled()) {
    logger.debug("Processing trade with id: " + id + " symbol: " + symbol);
}


On the other hand, if you use SLF4J, you can get the same result in much concise format as shown below :


logger.debug("Processing trade with id: {} and symbol : {} ", id, symbol);


In SLF4J, we don't need String concatenation and don't incur the cost of temporary not need String. Instead, we write log messages in a template format with a placeholder and supply actual values as parameters. You might be thinking about what if I have multiple parameters, well you can either use variable arguments version of log methods or pass them as an Object array. 

This is a really convenient and efficient way of logging. Remember, before generating final String for logging message, this method checks if a particular log level is enabled or not, which not only reduce memory consumption but also CPU time involved for executing those String concatenation instruction in advance. 

Here is the code of the SLF4J logger method from its Log4j Adapter class Log4jLoggerAdapter from slf4j-log4j12-1.6.1.jar.


public void debug(String format, Object arg1, Object arg2) {
    if (logger.isDebugEnabled()) {
      FormattingTuple ft = MessageFormatter.format(format, arg1, arg2);
      logger.log(FQCN, Level.DEBUG, ft.getMessage(), ft.getThrowable());
    }
}


It's also worth knowing that logging has a severe impact on the performance of applications, and it's always advised to only mandatory logging in a production environment.

How to use SLF4J with Log4J for logging

Apart from the above benefits, I think there is one caveat though, in order to use SLF4J you not only need to include SLF4J API Jar like slf4j-api-1.6.1.jar, but also companion JAR, depending upon which logging library, you are using in the backend. Suppose If you want to use SLF4J, Simple Logging Facade for Java,  along with Lo4J, you need to include following jars in your classpath, depending upon which version of SLF4J and log4J you are using e.g.

 slf4j-api-1.6.1.jar - JAR for SLF4J API
 log4j-1.2.16.jar    - JAR for Log4J API
 slf4j-log4j12-1.6.1.jar - Log4J Adapter for SLF4J

If you are using Maven to manage dependency in your project, you can just include SLF4J JAR, and maven will include its dependent companion JAR. In order to use Log4J along with SLF4J, you can include the following dependency in your project's pom.xml

<dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.6.1</version>
</dependency>
<dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.6.1</version>
</dependency> 

By the way, if you are interested in using a variable argument version of logger methods than include SLF4J 1.7 version.

Summary

To summarize this post, I would suggest the following reasons are good enough to choose SLF4J over Log4j, commons-logging, logback or java.util.logging directly.

1) Using SLF4J in your open-source library or internal library, will make it independent of any particular logging implementation, which means no need to manage multiple logging configurations for multiple libraries, your client will going to appreciate this.

2) SLF4J provides place holder based logging, which improves readability of code by removing checks lie isDebugEnabled(), isInfoEnabled() etc.

3) By using the SLF4J logging method, you defer cost of constructing logging messages (String), until you need it, which is both memory and CPU efficient.

4) As a side note, less number of temporary strings means less work for Garbage Collector, which means better throughput and performance for your application.

These advantages are just the tip of the iceberg, you will learn about more benefits when you start using SL4J and reading about it.  I strongly suggest any new code development in Java should use SLF4J for logging over any other logging API including log4J.

29 comments :

Lava said...

Thanks for sharing, bug i still think StringBuilder + log4j is a better choice for logging.

Anonymous said...

It is slf4j and not sl4j

Anonymous said...

For more reference about String pool with SLF4J please read this article:
http://learningviacode.blogspot.com/2012/09/string-pool-and-sl4j.html

Jozsef Szekrenyes said...

Thanks, that's good to know.

Prashanth said...

Check SLF4J in combination with Logback instead of Log4J too.

Anonymous said...

I think biggest advantage of using SLF4J over Log4J is better API. By the way, when using SLF4j, do you need to provide configuration file for both log4j and slf4j or only one?

Anonymous said...

Hi, I've seen sl4fj for sometime now but never had a good reason to put it in until i read your article; i personally didn't care about the facade benefits, but the string formatting benefits as well as the implied debug conditional checks etc. make it worth it!

Nice, small article that was easy to read and converted me.

Prashanth said...

In java.util.logging I perform

logger.log(Level.FINE, "client with id {0} identified as node ord {1}", new Object[]{id, nodeOrd});

is the option you mentioned "logger.debug("Processing trade with id: {} and symbol : {} ", id, symbol);"
better than the java.util.log way

Unknown said...

Javin,

Could you also write a blog post about Log4j 2.0 (http://logging.apache.org/log4j/2.x/)?

It has the same API that you mention, and it has some interesting performance improvements (like 12x more throughput in multi-threaded scenarios, and orders of magnitude(!) lower latency).

Musikele said...

I think the maven dependencies that you put are wrong, they are duplicated twice. Can you check / fix?

Anonymous said...

Good day! I just wish to give a huge thumbs up for the nice information you have right here on this
post. I can be coming again to your blog for more soon.



Also visit my web site; http://technologiesleader.blogspot.in/

Anonymous said...

Just keep in mind to use right set of SLF4j JAR with any particular logging framework, for example , slf4j-log4j12.jar and log4j-1.3.x.jar will not work together even though both of them are from Log4j, one is connector for Log4j 1.2 while other is log4j 1.3.

Unknown said...

Hi,

Just kind of get confused when you mentioned that string concatenation actually creates string in string pool. I thought for statements like

" Creating process with id " + id +"for processing "+process

Actually uses StringBuilder in the backend . So why is it created in the StringPool?

thanks

Kapil Ahuja said...

@Teong, each string literal is a String object, which is created inside pool. StringBuilder is only used for string concatenation, for example in above case, two String object will be created in pool for two String literals " Creating process with id" and "for processing ", and one StringBuilder to generate final string. I hope this helps.

Kapil Ahuja said...

God comparison between Log4j and SLF4j, but I think you forgot to mention one of the key property of SLF4j library. In order to make your library using SLF4j, you only need a single mandatory dependency, namely slf4j-api.jar. If no binding is found on the class path, then SLF4J will default to a no-operation implementation, but your client might cry while finding right set of JAR for Log4j or Logbak :)

Anonymous said...

When using the following with debug logging turned off

logger.debug("Message {}, {}, {}", new Object[]{obj1,obj2,obj3});

instead of

if(logger.isDebugEnabled()) {
logger.debug();
}

are you not still creating objects unnecessarily? Isn't the message string itself and the new object array created unnecessarily?

Anonymous said...


org.slf4j
slf4j-api
1.6.1
provided



org.slf4j
slf4j-log4j12
1.6.1
provided

Anonymous said...

Something the isDebugEnabled check does in log4j is save from the method call which includes creation of the String (argument of the method). So basically there will be no method call for log.debug if the debug is not enabled.
But if using slf4j we use the construct without any if check then the method call is happening and so is the String creation being done.

Unknown said...

Its a choice between efficient code and pretty code.
Good compiler save the day by compiling a pretty looking code to an efficient one

Unknown said...

Thats a great blog. The knowledge on saving cpu time by avoiding string concatenation was something new for me.

LUPU said...

I'm getting this warning in log:

log4j:WARN No appenders could be found for logger (org.apache.axiom.om.util.StAXUtils).
log4j:WARN Please initialize the log4j system properly.

I've done some reasearch on the Internet, but i have no log4j.propesties file.
Using slf4j-api-1.7.12.jar

Tanks a lot!

Sanjay said...

Nice article, I been using SLF4J from last two years but not aware about such benefits. Thanks.

phansson said...

My two cents: These days the benefits of using a logging framework different from JUL are really not enough to justify going outside the JDK. You compare to ancient Log4J and then say: "look, SLF4J is better" but you could have achieved the same with JUL. Correct me if wrong.

Anonymous said...

@phansson, JUL is not great on performance - http://blog.takipi.com/the-logging-olympics-a-race-between-todays-top-5-logging-frameworks/

Keep in mind that log4j was a dead project. So Ceki Gülcü (log4j creator) decided (perhaps due to apache politics) to create slf4j and logback as alternatives. But, things keep changing. Apache team (with new members) wake up and decide to rewrite log4j as log4j2. Today log4j2 has, in most cases, best performance. But slf4j seems to have wide use by now. Ceki has not, and likely will not write an implementation of slf4j to talk to log4j2. So in 2016, there is an agonizing choice if you have greenfield projects and you need to decide about logging. If you want to use the fastest (log4j2) and you don't want to be locked into the log4j API, then you must go back to the future and use JCL (commons-logging). If Ceki decided to support slf4j to log4j2, then slf4j could lockup most of the use of logging and we all can worry about something else.

Anonymous said...

I was able to find good advice from your blog posts.

javin paul said...

Thank you Anonymous, I appreciate your support.

Anonymous said...

Hi there! Do you use Twitter? I'd like to follow you if that would be okay.
I'm absolutely enjoying your blog and look forward to new updates.

javin paul said...

Hello Anonymous, Yes, I use twitter, @javinpaul, you can follow me there and thanks for kind words.

Unknown said...

In 2021, I don't think this recommendation still holds as SLF4J seems to be dormant: PR/issues/improvements are neglected and the 1.8/2.0 versions will remain forever in alpha/beta phase. In the meantime, Log4j2 is very active and can be also be used as a facade since they split API and core implementation.

Post a Comment