Is It Worth to Use Slf4J with Log4J2

Is it worth to use slf4j with log4j2

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 dependencieswith log4j-api as APIwith SLF4J as API
Log4j 2 as implementation2: log4j-api and log4j-core4: slf4j, log4j-slf4j-impl, log4j-api, log4j-core
Logback as implementation4: log4j-api, log4j-to-slf4j, slf4j, Logback2: slf4j and Logback

When using Log4J2, should I code to SLF4J or Log4J2 API?

As a developer on Log4j 2 I am, of course, biased. If you use Markers and filter on them I would recommend Log4j over SLF4J. SLF4J has some concurrency problems with Markers. If you want to take advantage of Log4j's support for logging Messages instead of Strings then, of course, you have to use Log4j's API. If you want to log using a printf style API you will have to use the Log4j API. If you want to use Java 8 lambda expressions you will have to use the Log4j API.

OTOH, if you you are perfectly happy with the SLF4J API and don't want any of the additional features the performance cost of using it should be minimal.

I should also note that it is possible to write to the Log4j 2 API and have that routed into SLF4J.

Was slf4j affected with vulnerability issue in log4j

Depends on the underlying implementation of SLF4J.
log4j 1.x is safe with respect to CVE-2021-44228. Thus, if your SLF4J provider/binding is slf4j-log4j12.jar, you are safe regarding CVE-2021-44228.

If you are using log4j-over-slf4j.jar in conjunction with the SLF4J API, you are safe unless the underlying implementation is log4j 2.x.

Check this - http://slf4j.org/log4shell.html

Is there an adapter for log4j2 to work over slf4j?

Log4j2 itself bundles a slf4j implementation (log4j-slf4j-impl-2.x.jar)

This is one of the jars in the Log4j2 distribution.


Update after the question was clarified:

Log4j2 includes a log4j-to-slf4j bridge “. This is what you need to route Log4j2 logging to another slf4j implementation.

The error mentioned is likely a problem of incompatible versions but the question doesn't mention version numbers so it's hard to say.

Difference between slf4j-log4j12 and log4j-slf4j-impl

Those artifacts use different versions of Log4j:

  • slf4j-log4j12 is a bridge between SLF4J and Log4j 1.2. Its versioning follows SLF4J.
  • log4j-slf4j-impl is a bridge between SLF4J 1.x (up to 1.7.x) and Log4j 2.x. Its versioning follows LOG4J2.
  • log4j-slf4j2-impl is a bridge between SLF4J 2.x (or higher) and Log4j 2.x. Its versioning follows LOG4J2.

Since Log4j 1.x reached end-of-life more than 7 years ago, there is no sense in using the first one in new software.

using slf4j with log4j2

Yes, async loggers via the disruptor should work whether your app uses the log4j2 api, the slf4j api or the log4j-1.2 api. See the log4j2 FAQ page for which jars to include. You'll need a jar for the slf4j api, and in addition you'll need the log4j-api-2.x, log4j-core-2.x and log4j-slf4j-impl-2.x jars. The same log4j2.xml config file should work.

Update: not all Log4j2 functionality is available via the SLF4J API.

Update 2: Is it safe to program directly to the Log4j2 API?

Can we use all features of log4j2 if we use it along with slf4j api?

The Log4j2 API is richer than the SLF4J API, and many Log4j2 API features are not accessible via SLF4J. See below for details.

Features of the Log4j2 implementation, like Async Loggers, Lookups, Filters, Layouts and Appenders are controlled with configuration and are available regardless of the logging API you use in your application.

Please also see this answer to the different but related question of why it is safe to program to the Log4j2 API.

10 Log4j2 API features not available in SLF4J

(1) The Message API allows applications to log structured objects in addition to just text. Internally Log4j2 converts everything that is logged to a Message, and exposing this to the API opens up all kinds of possibilities for applications to interact with downstream logging components (filters, layouts, appenders). This can be useful if you're developing custom components as plugins to Log4j2, as well as when you're using the built-in ones. For a built-in example, see how StructuredDataMessage is used for fine-grained control over Rfc5424Layout.

(2) Java 8 lambda support allows you to lazily create parameters or log messages without explicitly checking if the requested log level is enabled.

// Java-8 style optimization: no need to explicitly check the log level:
// the lambda expression is not evaluated if the TRACE level is not enabled
logger.trace("Some long-running operation returned {}", () -> expensiveOperation());

(3) Mixing {}-style parameters with String::format %s %d-style parameters. The {} style has better performance and can be used with any parameter type, but the printf style gives fine grained control over the formatting. Log4j2 allows you to easily mix these parameter styles. For example:

logger.debug("Opening connection to {}...", someDataSource);
logger.printf(Level.INFO, "Logging in user %1$s with birthday %2$tm %2$te,%2$tY", user.getName(), user.getBirthdayCalendar());

(4) CloseableThreadContext offers some extra convenience over the normal ThreadContext (MDC) in SLF4J: it automatically removes items when you're done. For example:

// Add to the ThreadContext map for this try block only;
try (final CloseableThreadContext.Instance ctc = CloseableThreadContext
.put("id", UUID.randomUUID().toString())
.put("loginId", session.getAttribute("loginId"))) {
logger.debug("Message 1");
// call some other code that also does logging
...
logger.debug("Message 2");
...
} // "id" and "loginId" are now removed from the ThreadContext map

(5) Log4j2's ThreadContext, in addition to key-value pairs, also has push and pop methods to support stack functionality (what used to be called NDC in Log4j 1).

(6) SLF4J does not support the FATAL log level.

(7) Log4j2 has support for custom log levels. These can be used with the log methods, for example: logger.log(Level.getLevel("FINE"), "... msg"), or you can generate a custom logger wrapper with convenience methods for your custom log levels.

(8) The Log4j2 API accepts any Object, not just Strings. This is one of the things that allow Log4j2 to be "garbage-free", meaning it will avoid allocating new Objects. Your Object is logged without creating any temporary Strings if it is a Number, a CharSequence or when it implements the (Log4j2) StringBuilderFormattable interface.

The Log4j2 API will also avoid creating vararg arrays if you log 10 parameters or less. SLF4J creates vararg arrays if you log more than 2 parameters.

(9) The above you get for free just by using the Log4j2 API directly. On top of that, if you really care about avoiding creating temporary objects (like some interactive games and low-latency financial applications do), you can avoid auto-boxing primitive parameters with the Unbox utility class.

(10) SLF4J Markers' use of coarse-grained synchronization may have performance impact for multi-threaded applications (SLF4J-240). See the Advanced Filtering section of this performance test results page.


Disclaimer: I contribute to Log4j2.

logging multiple messages with log4j2 binded with slf4j

UPDATE:
It seems to be a case for SLF4J MDC (Mapped diagnostic context) (or directly Log4j ThreadContext).

Java code:

MDC.put("ownId", "line-0" /* computeYourOwnIdHere() */);
logger.debug("A Sample Debug Message");

Configuration pattern:

<Property name="LOG_PATTERN"> %X{ownId} | %d{yyyy-MM-dd HH:mm:ss}{GMT+0} | %d{yyyy-MM-dd HH:mm:ss} | %p | %m%n </Property>

previous answer:

logger.info("message1", "message2"); is just an API to build formatted log message. Since the "message1" does not contain argument placeholder {}, the "message2" string is simply discarded. What concrete "other info coming from log4j2 pattern" are you trying to insert into logging message? Your requirement to put it in the middle of message is very unusual, putting aside the question how log analyzing tools would process such a log.
If "some other info coming from log4j2 pattern" message1 message2 would suffice, just use pattern "... %m" at the end and call log.info("message1 {}","message2").



Related Topics



Leave a reply



Submit