Log statements help record the flow of an application’s execution. Hence they are an important part of software coding process. For the Java language the common Log API libraries are Java Util Logger (commonly known as JUL), Apache Log4J, SLF4J etc. While these libraries support many advanced capabilities, most developers only use a handful of features. For example, ability to send log messages to files and the ability change log levels dynamically etc.

In this blog I will compare JUL against SLF4J and focus on these above mentioned features. Many will argue that this is not a fair comparison since SFL4J is really a facade and supports JUL as well. To clarify, I will compare the SimpleLogger that is bundled with SLF4J against direct JUL usage.

In order to setup SLF4J with SimpleLogger you just need to include both JAR files in your class path. Please refer to the SLF4J Manual.

Log API - Logging to Console

By default most Log API libraries support redirecting output to console. Let us look at a code snippet that shows how this is accomplished in JUL and SLF4J.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public class LoggerComparison1 {

    private static java.util.logging.Logger javaLogger = java.util.logging.Logger.getLogger("LoggerComparison1");
    private static org.slf4j.Logger slf4jLogger = org.slf4j.LoggerFactory.getLogger("LoggerComparison1");

    public static void main(String[] args)
    {
        javaLogger.severe("This message is from Java Util Logger");
        javaLogger.finer("This is a finer message from Java Util Logger");
        slf4jLogger.warn("This message is from SLF4J Logger");
        slf4jLogger.debug("This is a debug log from SLF4J Logger");
    }
}

The output of the above program is as follows:

Oct 06, 2013 11:05:29 PM LoggerComparison1 main                         ==> This is printed by JUL
SEVERE: This message is from Java Util Logger                           ==> This is printed by JUL
[main] WARN LoggerComparison1 - This message is from SLF4J Logger       ==> This is printed by SLF4J
  • You can see that Java Util Logger did not print the “finer” statement. This is because the default Log level is INFO
  • The more interesting thing to observe is that JUL prints two lines of logger output. This can be confusing while doing GREP.
  • The SLF4J Simple Logger does not print the time stamp by default. I strongly believe that most systematic users will end up configuring SLF4J such that timestamp is printed.
  • Overall, both JUL and SLF4J have some drawbacks while use them in their minimally initialized avatars.

Log API - including timestamp and changing log level

Let us see how we can include timestamp for SLF4J Simple Logger (JUL already includes it) and also check out how log levels can be changed.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class LoggerComparison2 {
    static {
        // set a system property such that Simple Logger will include timestamp
        System.setProperty("org.slf4j.simpleLogger.showDateTime", "true");
        // set a system property such that Simple Logger will include timestamp in the given format
        System.setProperty("org.slf4j.simpleLogger.dateTimeFormat", "dd-MM-yy HH:mm:ss");
        // set minimum log level for SLF4J Simple Logger at warn
        System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "warn");
    }
    private static java.util.logging.Logger javaLogger = java.util.logging.Logger.getLogger("LoggerComparison2");
    private static org.slf4j.Logger slf4jLogger = org.slf4j.LoggerFactory.getLogger("LoggerComparison2");

    public static void main(String[] args)
    {
        // set minimum log level for JUL at WARNING
        javaLogger.setLevel(java.util.logging.Level.WARNING);
        javaLogger.severe("This SEVERE Log is from Java Util Logger.");
        javaLogger.info("This INFO Log from Java Util Logger should not be printed.");
        slf4jLogger.warn("This WARN Log is from SLF4J.");
        slf4jLogger.debug("This DEBUG Log from SLF4J should not be printed.");
    }
}

If you compile and run the above program, here is the output you get:

Oct 20, 2013 10:35:27 PM LoggerComparison2 main
SEVERE: This SEVERE Log is from Java Util Logger.
20-10-13 22:35:27 [main] WARN LoggerComparison2 - This WARN Log is from SLF4J.
  • You can see that the timestamp limitation of SLF4J Simple Logger can be overcome very easily. In fact SLF4J Simple Logger uses system property to customize the logger and hence can be configured in many different ways with ease.
  • Also note that unlike JUL, the timestamp of SLF4J Simple Logger is included in the same line as the log statement output.
  • You can also see that by setting appropriate Log levels, we can include or exclude some statements from the output.

Log API - Redirecting output to a file

Finally, lets look at one more commonly used log API feature - redirecting output to a file. Here is the program:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import java.io.IOException;
import java.util.logging.FileHandler;

public class LoggerComparison3 {
    static {
        // set a system property such that Simple Logger will include timestamp
        System.setProperty("org.slf4j.simpleLogger.showDateTime", "true");

        // set a system property such that Simple Logger will include timestamp in the given format
        System.setProperty("org.slf4j.simpleLogger.dateTimeFormat", "dd-MM-yy HH:mm:ss");

        // set minimum log level for SLF4J Simple Logger at warn
        System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "warn");

        // configure SLF4J Simple Logger to redirect output to a file
        System.setProperty("org.slf4j.simpleLogger.logFile", "SLF4J-File.log");
    }
    private static java.util.logging.Logger javaLogger = java.util.logging.Logger.getLogger("LoggerComparison3");
    private static org.slf4j.Logger slf4jLogger = org.slf4j.LoggerFactory.getLogger("LoggerComparison3");

    public static void main(String[] args)
    {
        // configure File Handler for JUL
        addFileHandlerToJavaLogger(javaLogger);

        // set minimum log level for JUL at WARNING
        javaLogger.setLevel(java.util.logging.Level.WARNING);

        javaLogger.severe("This SEVERE Log is from Java Util Logger.");
        javaLogger.info("This INFO Log from Java Util Logger should not be printed.");
        slf4jLogger.warn("This WARN Log is from SLF4J.");
        slf4jLogger.debug("This DEBUG Log from SLF4J should not be printed.");
    }

    private static void addFileHandlerToJavaLogger(java.util.logging.Logger logger)
    {
        try {
            logger.addHandler(new FileHandler("JUL-File.log"));
        } catch (IOException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }
    }
}

If you compile and run the above, you will notice several interesting results.

  • It is much easier to configure output redirection in SLF4J vs. Java Logger. In SLF4J you simply set the appropriate property whereas in Java Logger you need to create an instance of FileHandler.
  • Even though Java Logger is configured with FileHandler, a copy of the log statements is redirected to Console also. This may not be the desired result everytime. This is as per design of Java Logger where multiple handlers are supported. If you want to have only one Handler in JUL, you need to write additional code.
  • Probably the most peculiar result of this program, is that the output file for JUL contains XML message where as SLF4J output file contains the logger output as was seen on the console earlier.
  • This is yet again an unwanted design intent of Java Logger. Each Handler can have a Formatter associated with it. And the output format is controlled by the Formatter. For some weird reason, the FileHandler of JUL uses an XMLFormatter by default.

Log API - Conclusion

With these three simple programs, it can be seen that with SLF4J you can hit the ground running. For the most common and also the most used Log API features, SLF4J (Simple Logger) provides much simple APIs when compared to Java Util Logger.