Is it worth to use slf4j with log4j2
Asked Answered
M

3

189

I am not able to decide whether to use slf4j or not with log4j2. Based on online posts, does not look like it will have any performance hit but is it really required.

Also these points rule in favor of log4j2:

  • SLF4J forces your application to log Strings. The Log4j 2 API supports logging any CharSequence if you want to log text, but also supports logging any Object as is.
  • The Log4j 2 API offers support for logging Message objects, Java 8 lambda expressions and garbage-free logging (it avoids creating vararg arrays and avoids creating Strings when logging CharSequence objects).
Mesnalty answered 6/1, 2017 at 2:31 Comment(4)
Maybe. What if my application server includes slf4j and logback (or log4jv1)? Should I then be forced to install a third logger to use your application? Or maybe corporate security decides you may only use java.util.logging in production, what then?Ernie
Thanks for writing. But, if every one is using log4j org wide the above argument will not hold valid.Mesnalty
Using SLF4J means that replacing the implementation is very easy if company policy changes, e.g. when your company is acquired and new policies forced on you. Using SLF4J now, when you write the code, will take no more time than using Log4j directly. Replacing direct Log4j calls later will take a lot of time. SLF4J is a free investment/insurance for the future. Is that more important than Log4j 2 API features? Only you (or company policy) can decide that.Tinderbox
Is there even a way to use slf4j with log4j2 if you wanted to? This page shows usage with log4j - version 1.2 - which is end of life'd), but no option for log4j2. If there is a way, why does slf4j not mention it?Weingarten
U
254

Go ahead: program to the log4j2 API instead of slf4j

It's safe: the Log4j2 API offers the exact same guarantees as slf4j - and more.

Now that Log4j2 itself is separated into an API and an implementation module, there is no longer any value in using SLF4J.

Yes, it is good engineering practice to keep your options open. You may want to change to another logging implementation later.

For the last 10 years or so, building such flexibility in your application meant using a wrapper API like SLF4J. This flexibility doesn't come for free though: the disadvantage of this approach is that your application cannot use the richer feature set of the underlying logging library.

Log4j2 offers a solution that doesn't require that your application is restricted to the lowest common denominator.

The escape valve: log4j-to-slf4j

Log4j2 includes a log4j-to-slf4j bridge module. Any application coded against the Log4j2 API can choose to switch the backing implementation to any slf4j-compliant implementation at any time.

log4j-to-slf4j

As mentioned in the question, using the Log4j2 API directly offers more functionality and has some non-functional advantages versus using a wrapper API like slf4j:

  • Message API
  • Lambdas for lazy logging
  • Log any Object instead of just Strings
  • Garbage-free: avoid creating varargs or creating Strings where possible
  • CloseableThreadContext automatically removes items from the MDC when you're finished with them

(See 10 Log4j2 API features not available in SLF4J for more details.)

Applications can safely use these rich features of the Log4j2 API without being locked in to the native Log4j2 core implementation.

SLF4J is still your safety valve, it just doesn't mean your application should code against the SLF4J API anymore.


Disclosure: I contribute to Log4j2.


Update: There seems to be some confusion that programming to the Log4j2 API somehow introduces a "facade for a facade". There is no difference in this respect between the Log4j2 API and SLF4J.

Both APIs require 2 dependencies when using a native implementation, and 4 dependencies for a non-native implementation. SLF4J and the Log4j2 API are identical in this respect. For example:

Required dependencies with log4j-api as API with SLF4J as API
Log4j 2 as implementation 2: log4j-api and log4j-core 4: slf4j, log4j-slf4j-impl, log4j-api, log4j-core
Logback as implementation 4: log4j-api, log4j-to-slf4j, slf4j, Logback 2: slf4j and Logback
Unders answered 6/1, 2017 at 6:42 Comment(20)
The API and implementation separation does not make sense as long as the API and implementation are tightly coupled. I reckon log4j2 API and impl are tightly coupled. Yes? In the case of SLF4J API, there are many implementations, including for java.logging, log4j 1, log4j 2, logback and in Groovy, Scala, Clojure...Slow
The Log4j2 API does not depend on the Log4j2 implementation. The dependency is strictly one way, as it should be. As I said, there's no lock-in to the native implementation.Unders
I understand. Let me rephrase my question. Are there any independent implementations of log4j2 API other than log4j2?Slow
The Log4j2 API and impl are not "tightly coupled". All these SLF4J implementations are all available: An application coded against the Log4j2 API can select the log4j-to-slf4j dependency instead of log4j-core and choose any of these SLF4J implementations you mentioned. The number of native implementations of the Log4j2 API is irrelevant.Unders
I agree. And just calling the Log4j2 API and implementation "tightly coupled" doesn't make that true either. Tight coupling is a [well-defined technical term][1] that does not apply here. If we follow your logic, every Java interface that only has one implementation class would be tightly coupled to that class. This is clearly not the case. [1]: en.m.wikipedia.org/wiki/Coupling_(computer_programming)Unders
I should have paid attention to your remarks about "log4j-to-slf4j" adapter module which make my comments above rather moot.Slow
Problem is that you often have dependencies on libraries which themself use slf4j, so it's easier to just stick with that.Enuresis
so, I should use an interface for an interface for an implementation? Yea, no thanks... Slf4j beat log4j to the punch by providing a good interface to implementation for logging... log4j2 should just implement slf4j api - if there's a missing feature, contribute it back, if slf4j won't take the new feature, then MAYBE there's a case for log4j2 api interfaces....Hime
@Hime Log4j2 does implement the slf4j api, just include the log4j-slf4j-impl dependency. It also provides its own API with more features and some non-functional benefits. SLF4J is free to adopt these (it is open source after all) but it naturally needs to worry about backwards compatibility. My point is that it's safe to program directly to the Log4j2 API, since it provides the same guarantees about being able to swap implementations as SLF4J provides.Unders
@RemkoPopma - you're still advocating against using the log4j interface. Yes, I get it - I can chain log4j2's interface -> slf4j's interface -> whatever implementation, but I'd just rather not abstract the abstraction - thanks but no thanks.Hime
@Hime I assume you're using the Log4j2 implementation. In that case it's simply Log4j2 API-> Log4j2 impl. I can't see why anyone would want to do slf4j-> Log4j2 API-> Log4j2 impl. That would be abstracting the abstraction and that is what I'm advocating against.Unders
Re: In that case it's simply Log4j2 API-> Log4j2 impl.....@RemkoPopma - dude, you are not getting it... I know HOW it works, what you're advocating for is me going in and changing all my code to use log4j2 instead of slf4j. No.Hime
I agree, for an existing application you should be fine with using the log4j-slf4j-impl module instead of modifying all your code. I must have been unclear, sorry. Not needing to modify code is what this is all about, and what I was trying to say is that the Log4j2 API does just as good a job at that as slf4j. For new projects, I encourage people to use Log4j2 directly.Unders
@Ceki: Will SLF4J fully support Log4j 2's features?Jimmyjimsonweed
@Jimmyjimsonweed SLF4J probably won't in its current form support the bulk of Log4j2's features. Doing so would require redesigning SLF4J into something that would essentially match the Log4j2 API, and would not be backwards compatible with non-Log4j2 implementations. The entire point of log4j2 is it's API. Nearly all of the 'backend' functionality of log4j2 is available in Logback. The entire reason Log4j2 exists is to address limitations of 'classic' APIs like SLF4J. With the new API, we can move to async loggers instead of async appenders, use lambdas to instead of logger guards, and use rich messagesMclean
Actually, there is a performance drawback of log4j2 + log4j-to-slf4j over slf4j-api according to the log4j-to-slf4j documentation: Use of this adapter may cause some loss of performance as the Log4j 2 Messages must be formatted before they can be passed to SLF4J. With Log4j 2 as the implementation these would normally be formatted only when they are accessed by a Filter or Appender.Rileyrilievo
@Rileyrilievo Isn't the performance loss same as it would be using SLF4J directly?Fictional
Unfortunately there are no hard numbers on the performance differences between using pure Log4j2 and a logging facade.Unders
If using the Log4J 2 API, will every single one of the APIs and their features degrade gracefully when choosing to use the SLF4J bridge? Or will some things be broken? I.E. Would the log file end up identical in all cases?Ramble
The only gap that I have found that exists in the API for SLF4J but does not exist in Log4J2 is the ability to get all the existing loggers or log levels at runtime. Although you can set them in Log4J, there doesn't seem to be an API to get them. This functionality is useful for building front ends where the log level can be toggled at runtime by the user. Unfortunately, it seems like we are forced to go with SLF4J if we want that ability.Tray
D
19

There are quite a few considerations which make logging "more complicated than it seems at first glance", (hence the multi-decade of bitter in-fighting!).

Separation of Concerns

At the end of the day, code 'sends log data' and that data 'ends up somewhere'. But where it ends up depends on the purpose for which it is being gathered. Greatly complicating the picture is the fact that modern software is composed of various components, and they all potentially have a need to log.

Let's consider a worst-case: all components use System.out#println(String). At least all the statements are in execution-order, but it might not be simple to discern which component generated each piece of output. And some components might be overly verbose for the context in which they are used.

Let's consider a next-worst-case: all components make their own arrangements for controlling their logging behaviour and destination. The admin might have to configure dozens of log systems for a single piece of software. Now the log statements are not together and they're out of order. Hopefully they all have a consistent timestamp strategy!

We would like something in-between: something whereby code can say 'log this' and an admin can control where it ends up.

An overly brief history

Enter Log4J v1, which tackled the problem with notions of 'levels', 'appenders', 'filters' 'layouts' and 'contexts' ... a conceptual architecture supported by a hierarchical 'logger namespace' (including a way to naturally leverage off Java package namespaces), plus a configuration mechanism for easy management.

That was all fine and good ... so long as all the components in the software relied on the same version! There was a time when these things were in flux. The main contribution of SLF4J was to 'harden' these concepts into a stable API from the point of view of the component-developer, without prejudicing the options of the admin for doing their part of the work. Libraries could rely on the SLF4J 'facade', expecting that they'd be talking to the 'implementation' just a few calls down the stack. Administrators could choose what suited them to compose the logs into a coherent record they cared about.

(It is even more complicated when your software runs in a container, and the container has its own logging needs, and you're not even the same app running in the container ... Tomcat's JULI logging - used for its own internal logging - 'gets out of the way' of apps running in classloader sub-contexts.)

Mysteriously scorning the work by Log4J, the Java Community Process decided to implement much-the-same conceptual architecture in java.util.logging, with arguably less flexibility in the details. However, with j.u.l being essentially a sub-set of the semantic richness of SLF4J, it was easy make SLF4J a facade to j.u.l.

Apache's Commons Util Logging was probably not very necessary. Ceki's own Logback introduced management features which Log4J v1 did not have at the time - not least being an implementation of SLF4J and solving all those very-real classloader headaches, but also providing a competent implementation with some appealing features for the administrator.

Logging for different situations

But logging is done in a lot of different contexts. Whacking those messages out to super-slow I/O without unduly blocking the thread, and not paying the price of computing the log message unless it's needed, and producing coherent logs in multi-threaded contexts ... these things all matter. (Which is why java.util.logging is not often used!).

Sometimes, the required optimisations will impact on the conceptual architecture, which in turn must impact on the developer-side API. For example, the opportunities presented by closures will definitely speed things up if the log message ends up being a no-op because of filtering. Either a SLF4J.next or some other API needs to be considered to make use of that feature, and Log4J2 need not be excluded from this decision. With the API portion being a conceptual super-set of that offered by SLF4J, it is simple to make it a facade either for SLF4J and those implementations under it ... or more direct bridges to what the administrator has preferred.

For the app-developer, it really doesn't matter, so long as ultimately, there is one logging facility chosen by the administrator, and all the components can get a log out to it. If the facility can accept messages via SLF4J and Log4J2-the-API, then that's great. Log4J2-the-implementation does just this. You can have your cake and eat it too: your application can enjoy the opportunities offered by Log4J2-the-API while still using libraries that are adequately catered-for by SLF4J. If the administrator scorns Log4J2-the-implementation (though it is hard to see why they would, from any angle), then they can use anything that already supports SLF4J without waiting for that logging implementation to support Log4J2-the-API. You can have your cake and eat it.

For library developers, it is more of an issue. The safe path is SLF4J because of its widespread adoption. If logging is critical to the success of your library ... particularly if it is multi-threaded, log statements are potentially expensive to produce, and it might be better to omit the processing if they will not ultimately be consumed, if there is a large volume of log statements to handle, performance is critical, and your users are likely to appreciate the benefits of Log4J2-the-implementation, then do Log4J2. But you're not stealing opportunities from your users by remaining with SLF4J, either. The admin can still use Log4J-the-implmenation if they wish.

The bottom line

If you want the features afforded by Log4J2 then go for them. If you don't need them, SLF4J is a mature, stable interface with a lot of support. SLF4J remains an important piece of the open-source infrastructure. Ceki has done the community a great service, for a lot of griping in return.

But rich APIs supported by competent implementations prevail in the end. Today's stability is tomorrows stagnation. The process of refinement keeps going. No need to get off the bus so long as it is going where you want to go.

Detached answered 17/3, 2021 at 4:10 Comment(1)
I have a couple of comments on this. commons-logging was "necessary" as it was created in 2001, about 5 years before SLF4J. As for SLF4J support, from the beginning of 2020 through today Ceki has closed 2 PRs, merged none, performed 1 commit to record an ICLA, replied to 3 emails in the first half of 2020 (none since) and posted to one Jira issue to ask for an ICLA. He made one minor commit to Logback this year and posts occasionally on twitter so I think he is OK but I am not sure what is going on. But I agree that he deserves tons of credit.Endurable
C
0

Sometimes the choice is not yours - it has been taken by others. Just imagine you decide for one of (log4j2, slf4j). And at some point in time you add another dependency to your project. What is the chance that the developers of that jar took the exact same decision?

So in one project you may end up with both, or even more such logging facades in parallel. To not go nuts you can still at runtime forward the log output to one framework only.

For the two frameworks log4j2 and slf4j there are two different bridges:

Canaliculus answered 30/1 at 22:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.