Frequently Asked Questions about SLF4J

Generalities

  1. What is SLF4J?
  2. When should SLF4J be used?
  3. Is SLF4J yet another loggingfacade?
  4. If SLF4J fixes JCL, then why wasn't the fix made in JCL instead of creating a new project?
  5. When using SLF4J, do I have to recompile my application to switch to a different logging system?
  6. What are SLF4J's requirements?
  7. Why is SLF4J licensed under X11 type license instead of the Apache Software License?
  8. Where can I get a particular SLF4J binding?
  9. Should my library attempt to configure logging?
  10. What about Maven 2 transitive dependencies?
About the SLF4J API
  1. Why don't the printing methods in the Logger interface accept message of type Object, but only messages of type String?
  2. Can I log an exception without an accompanying message?
  3. What is the fastest way of (not) logging?
  4. How can I log the string contents of a single (possibly complex) object?
  5. Why doesn't the org.slf4j.Logger interface have methods for the FATAL level?
  6. Why was the TRACE level introduced only in SLF4J version 1.4.0?
Implementing the SLF4J API
  1. How do I make my logging framework SLF4J compatible?
  2. How can my logging system add support for the Marker interface?
General questions about logging
  1. Should Logger members of a class be declared as static?

Generalities

What is SLF4J?

SLF4J is a simple facade for logging systems allowing the end-user to plug-in the desired logging system at deployment time.

[top]

When should SLF4J be used?

In short, libraries and other embedded components should consider SLF4J for their logging needs because libraries cannot afford to impose their choice of logging system on the end-user. On the other hand, it does not necessarily make sense for stand-alone applications to use SLF4J. Stand-alone applications can invoke the logging system of their choice directly.

SLF4J is only a facade, meaning that it does not provide a complete logging solution. Operations such as configuring appenders or setting logging levels cannot be performed with SLF4J. Thus, at some point in time, any non-trivial application will need to directly invoke the underlying logging system. In other words, complete independence from the API underlying logging system is not possible for a stand-alone application. Nevertheless, SLF4J reduces the impact of this dependence to near-painless levels.

Suppose that your CRM application uses log4j for its logging. However, one of your important clients request that logging be performed through JDK 1.4 logging. If your application is riddled with thousands of direct log4j calls, migration to JDK 1.4 would be a long and error-prone process. Had you been invoking SLF4J API instead of log4j, the migration could be completed in a matter of minutes instead of hours.

SLF4J lets component developers to defer the choice of the logging system to the end-user but eventually a choice needs to be made.

[top]

Is SLF4J yet another logging facade?

SLF4J is conceptually similar to JCL. As such, it can be thought of as yet another logging facade. However, SLF4J is orders of magnitude simpler in design and arguably more robust.

[top]

If SLF4J fixes JCL, then why wasn't the fix made in JCL instead of creating a new project?

This is a very good question. First, SLF4J static binding approach is very simple, perhaps even laughably so. It was not easy to convince developers of the validity of the approach. It is only after SLF4J was released and started to become accepted did the approach gain respectability in the relevant community.

Second, SLF4J offers two enhancements which developers currently tend to underestimate. Parameterized log messages solve an important problem associated with logging performance in a pragmatic way. Marker objects, which are supported by the org.slf4j.Logger interface, pave the way for adoption of advanced logging systems and still leave the door open to switching back to more traditional logging systems if need be.

[top]

When using SLF4J, do I have to recompile my application to switch to a different logging system?
No, you do not need to recompile your application. You can switch to a different logging system by removing the previous SLF4J binding and replacing it with the binding of your choice.

For example, if you were using the NOP implementation and would like to switch to log4j version 1.2, simply replace slf4j-nop.jar with slf4j-log4j12.jar on your class path but do not forget to add log4j-1.2.x.jar as well. Want to switch to JDK 1.4 logging? Just replace slf4j-log4j12.jar with slf4j-jdk14.jar.

[top]

What are SLF4J's requirements?

In principle, SLF4J requires JDK 1.3 or above, in particular slf4j-api is compatible with JDK 1.3. However, the underlying logging system might have a higher requirement. For instance, the slf4j-jdk14 binding requires JDK 1.4. Logback requires JDK 1.5, unless you are using the 1.4 retro-translated jars.

 

Binding Requirements
slf4j-nop JDK 1.3
slf4j-simple JDK 1.3
slf4j-log4j12 JDK 1.3, plus any other library dependencies required by the log4j appenders in use
slf4j-jdk14 JDK 1.4 or above
logback-classic JDK 1.5 or above, unless you are using the 1.4 retro-translated jars, plus any other library dependencies required by the logback appenders in use
[top]

[top]

Why is SLF4J licensed under X11 type license instead of the Apache Software License?

SLF4J is licensed under a permissive X11 type license instead of the ASL or the LGPL because the X11 license is deemed by both the Apache Software Foundation as well as the Free Software Foundation as compatible with their respective licenses.

[top]

Where can I get a particular SLF4J binding?

SLF4J bindings for SimpleLogger, NOPLogger, Log4jLoggerAdapter and JDK14LoggerAdapter are contained within the files slf4j-nop.jar, slf4j-simple.jar, slf4j-log4j12.jar, and slf4j-jdk14.jar. These files ship with the official SLF4J distribution. Please note that all bindings depend on slf4j-api.jar.

The binding for logback-classic ships with the logback distribution. However, as with all other bindings, the logback-classic binding requires slf4j-api.jar.

[top]

Should my library attempt to configure logging?

Embedded components such as libraries do not need and should not configure the logging system. They invoke SLF4J to log but should let the end-user configure the logging environment. When embedded components try to configure logging on their own, they often override the end-user's wishes. At the end of the day, it is the end-user who has to read the logs and process them. She should be the person to decide how she wants her logging configured.

[top]
What about Maven2 transitive dependencies?

As an author of a library built with Maven2, you might want to test your application using a binding, say slf4j-log4j12 or logback-classic, without forcing log4j or logback-classic as a dependency upon your users. As of SLF4J version 1.3, this quite easy to accomplish. But first, since your library's code depends on the SLF4J API you will need to declare slf4j-api as a compile-time (default scope) dependency.

 

<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.4.3</version> </dependency>

 

Limiting the transitivity of the SLF4J binding used in your tests can be accomplished by declaring the scope of the SLF4J-binding dependency as "test". Here is an example:

 

<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.4.3</version> <scope>test</scope> </dependency>

Thus, as far as your users are concerned you are exporting slf4j-api as a transitive dependency of your library, but not any SLF4J-binding or an underlying logging system.

About the SLF4J API

Why don't the printing methods in the Logger interface accept message of type Object, but only messages of type String?

In SLF4J 1.0beta4, the printing methods such as debug(), info(), warn(), error() in the Logger interface were modified so as to accept only messages of type String instead of Object.

Thus, the set of printing methods for the DEBUG level became:

debug(String msg); debug(String format, Object arg); debug(String format, Object arg1, Object arg2); debug(String msg, Throwable t);

Previously, the first argument in the above methods was of type Object.

This change enforces the notion that logging systems are about decorating and handling messages of type String, and not any arbitrary type (Object).

Just as importantly, the new set of method signatures offer a clearer differentiation between the overladed methods whereas previously the choice of the invoked method due to Java overloding rules were not always easy to follow.

It was also easy to make mistakes. For example, previously it was legal to write:

logger.debug(new Exception("some error"));

Unfortunately, the above call did not print the stack trace of the exception. Thus, a potentially crucial piece of information could be lost. When the first parameter is restricted to be of type String, then only the method

debug(String msg, Throwable t)

can be used to log exceptions. Note that this method ensures that every logged exception is accompanied with a descriptive message.

[top]

Can I log an exception without an accompanying message?

In short, no.

If e is an Exception, and you would like to log an exception at the ERROR level, you must add an accompanying message. For example,

logger.error("some accompanying message", e);

You might correctly observe that not all exceptions have a meaningful message to accompany them. Moreover, a good exception should already contain a self explanatory description. The accompanying message may therefore be considered redundant.

While these are valid arguments, there are three opposing arguments also worth considering. First, on many, albeit not all occasions, the accompanying message can convey useful information nicely complementing the description contained in the exception. Frequently, at the point where the exception is logged, the developer has access to more contextual information than at the point where the exception is thrown. Second, it is not difficult to imagine more or less generic messages, e.g. "Exception caught", "Exception follows", that can be used as the first argument for error(String msg, Throwable t) invocations. Third, most log output formats display the message on a line, followed by the exception on a separate line. Thus, the message line would look inconsistent without a message.

In short, if the user were allowed to log an exception without an accompanying message, it would be the job of the logging system to invent a message. This is actually what the throwing(String sourceClass, String sourceMethod, Throwable thrown) method in java.util.logging package does. (It decides on its own that accompanying message is the string "THROW".)

It may initially appear strange to require an accompanying message to log an exception. Nevertheless, this is common practice in all log4j derived systems such as java.util.logging, logkit, etc. and of course log4j itself. It seems that the current consensus considers requiring an accompanying message as a good a thing (TM).

[top]

What is the fastest way of (not) logging?

SLF4J supports an advanced feature called parameterized logging which can significantly boost logging performance for disabled logging statement.

For some Logger logger, writing,

logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));

incurs the cost of constructing the message parameter, that is converting both integer i and entry[i] to a String, and concatenating intermediate strings. This, regardless of whether the message will be logged or not.

One possible way to avoid the cost of parameter construction is by surrounding the log statement with a test. Here is an example.

if(logger.isDebugEnabled()) { logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i])); }

This way you will not incur the cost of parameter construction if debugging is disabled for logger. On the other hand, if the logger is enabled for the DEBUG level, you will incur the cost of evaluating whether the logger is enabled or not, twice: once in debugEnabled and once in debug. This is an insignificant overhead because evaluating a logger takes less than 1% of the time it takes to actually log a statement.

Better alternative based on parameterized messages

There exists a very convenient alternative based on message formats. Assuming entry is an object, you can write:

Object entry = new SomeObject(); logger.debug("The entry is {}.", entry);

After evaluting whether to log or not, and only if the decision is affirmative, will the logger implementation format the message and replace the '{}' pair with the string value of entry. In other words, this form does not incur the cost of parameter construction in case the log statement is disabled.

The following two lines will yield the exact same output. However, the second form will outperform the first form by a factor of at least 30, in case of a disabled logging statement.

logger.debug("The new entry is "+entry+"."); logger.debug("The new entry is {}.", entry);

A two argument variant is also availalble. For example, you can write:

logger.debug("The new entry is {}. It replaces {}.", entry, oldEntry);

If three or more arguments need to be passed, you can make use of the Object[] variant. For example, you can write:

logger.debug("Value {} was inserted between {} and {}.", new Object[] {newVal, below, above});

SLF4J uses its own message formatting implementation which differs from that of the Java platform. This is justified by the fact that SLF4J's implementation performs several orders of magnitude faster but at the cost of being non-standard and less flexible.

[top]

How can I log the string contents of a single (possibly complex) object?

In relatively rare cases where the message to be logged is the string form of an object, then the parameterized printing method of the appropriate level can be used. Assuming complexObject is an object of certain complexity, for a log statement of level DEBUG, you can write:

logger.debug("{}", complexObject);

The logging system will invoke complexObject.toString() method only after it has ascertained that the log statement was enabled. Otherwise, the cost of complexObject.toString() conversion will be advantageously avoided.

[top]

Why doesn't the org.slf4j.Logger interface have methods for the FATAL level?

From the stand point of a logging system, the distinction between a fatal error and an error is usually not very useful. Most programmers exit the application when a fatal error is encountered. However, a logging library cannot (and should not) decide on its own to terminate an application. The initiative to exit the application must be left to the developer.

Thus, the most the FATAL level can do is to highlight a given error as the cause for application to crash. However, errors are by definition exceptional events that merit attention. If a given situation causes errors to be logged, the causes should be attended to as soon as possible. However, if the "error" is actually a normal situation which cannot be prevented but merits being aware of, then it should be marked as WARN, not ERROR.

Assuming the ERROR level designates exceptional situations meriting close attention, we are inclined to believe that the FATAL level is superfluous.

[top]

Why was the TRACE level introduced only in SLF4J version 1.4.0?

The addition of the TRACE level has been frequently and hotly debated request. By studying various projects, we observed that the TRACE level was used to disable logging output from certain classes without needing to configure logging for those classes. Indeed, the TRACE level is by default disabled in log4j and logback as well most other logging systems. The same result can be achieved by adding the appropriate directives in configuration files.

Thus, in many of cases the TRACE level carried the same semantic meaning as DEBUG. In such cases, the TRACE level merely saves a few configuration directives. In other, more interesting occasions, where TRACE carries a different meaning than DEBUG, Marker objects can be put to use to convey the desired meaning. However, if you can't be bothered with markers and wish to use a logging level lower than DEBUG, the TRACE level can get the job done.

Note that while the cost of evaluating a disabled log request is in the order of a few nanoseconds, the use of the TRACE level (or any other level for that matter) is discouraged in tight loops where the log request might be evaluated millions of times. If the log request is enabled, then it will overwhelm the target destination with massive output. If the request is disabled, it will waste resources.

In short, although we still discourage the use of the TRACE level because alternatives exist or because in many cases log requests of level TRACE are wasteful, given that people kept asking for it, we decided to bow to popular demand.

[top]

Implementing the SLF4J API

How do I make my logging framework SLF4J compatible?

Adding supporting for the SLF4J is suprisingly easy. Essentialy, you coping an existing binding and tailoring it a little (as explained below) does the trick.

Assuming your logging system has notion of a logger, called say MyLogger, you need to provide an adapter for MyLogger to org.slf4j.Logger interface. Refer to slf4j-jcl, slf4j-jdk14, and slf4j-log4j12 modules for examples of adapters.

Once you have written an appropriate adapter, say MyLoggerAdapter, you need to provide a factory class implementing the org.slf4j.ILoggerFactory interface. This factory should return instances MyLoggerAdapter. Let MyLoggerFactory be the name of your factory class.

Once you have the adapter, namely MyLoggerAdapter, and a factory, namely MyLoggerFactory, the last remaining step is to modify the StaticLoggerBinder class so that it reurns an new instance of MyLoggerFactory. You will also need to modify the loggerFactoryClassStr variable.

For Marker or MDC support, you could use the one of the existing NOP implementations.

In summary, to create an SLF4J binding for your logging system, follow these steps:

  1. start with a copy of an existing module,
  2. create an adapter between your logging system and org.slf4j.Logger interface
  3. create a factory for the adapter created in the previous step,
  4. >modify StaticLoggerBinder class to use the factory you created in the previous step
[top]

How can my logging system add support for the Marker interface?

Markers consitute a revolutionary concept which is supported by logback but not other existing logging systems. Consequently, SLF4J confromant logging systems are allowed to ignore marker data passed by the user.

However, even though marker data may be ignored, the user must still be allowed to specify marker data. Otherwise, users would not be able to switch between logging systems that support markers and those that do not. In order to provide minimal support for markers, SLF4J conformant systems need to to include certain Marker related classes, namely, org.slf4j.Marker, org.slf4j.IMarkerFactory, org.slf4j.MarkerFactory, org.slf4j.impl.BasicMarker, org.slf4j.impl.BasicMarkerFactory, org.slf4j.impl.MarkerIgnoringBase, org.slf4j.impl.StaticMarkerBinder and org.slf4j.spi.MarkerFactoryBinder. Al of these classes are availalbe in the SLF4J subversion repository.

The MarkerIgnoringBase class can serve as a base for adapters or native implementations of logging systems lacking marker support. In MarkerIgnoringBase, methods taking marker data simply invoke the corresponding method without the Marker argument, discarding any Marker data passed as argument. Your SLF4J adapters can extend MarkerIgnoringBase to quickly implement the methods in org.slf4j.Logger which take a Marker as the first argument.

[top]

General questions about logging

Should Logger members of a class be declared as static?

This author recommends that loggers members be declared as instance variables instead of static.

Static logger members cost a single variable reference for all instances of the class whereas an instance logger member will cost a variable reference for every instance of the class. For simple classes instantiated thousands of times there might be a noticeable difference.

However, more recent logging systems, e.g log4j or logback, support a distinct logger context for each application running in the application server. Thus, even if a single copy of log4j.jar or logback-classic.jar is deployed in the server, the logging system will be able to differentiate between applications and offer a distinct logging environment for each application.

More specifically, each time a logger is retrieved by invoking LoggerFactory.getLogger() method, the underlying logging system will return an instance appropriate for the current application. Please note that within the same application retrieving a logger by a given name will always return the same logger. For a given name, a different logger will be returned only for different applications.

If the logger is static, then it will only be retrieved once when the hosting class is loaded into memory. If the hosting class is used in only in one application, there is not much to be concerned about. However, if the hosting class is shared between several applications, then all instances of the shared class will log into the context of the application which happened to fist load the shared class into memory - hardly the behavior expected by the user.

In summary, logger members should be instance variables, except for classes with few members and instantiated very (very!) frequently.

[top]