System.out.println vs Loggers

Introduction

As a programmer, it’s often important to know what your program is doing, and debug statements can help a lot in understanding error conditions and thee actual behaviour of a program. However, to create such information, there are a couple of possible solutions available. For example, we can use System.out.println statements in our program to give feedback to the users of our program, or introduce a full fledged logging system. In this tutorial, we’re going to look at the differences between the two, and help you decide which approach works best for you.

System.out.println

By default, the JVM provides access to System.out, without needing any references to external libraries. Whenever we use System.out, it gives us a reference to the standard output stream of the JVM. This output stream can be used to write output, which will be displayed on the standard output. Output is typically text, but it can also be a number or an object, since the println method is overloaded for all types. An example can be seen below:

System.out.println("Hello World");

What’s happening in the above is that the println method of the static PrintStream out reference is invoked, which will result in a displaying the message to the standard output. However ,It’s also possible to override the standard output by using the System.setOut, and for example write to a file instead:

FileOutputStream fos = new FileOutputStream(File.createTemp());
System.setOut(new PrintStream(fos));
System.out.println("Hello World written to temp file");

Besides the println method, the PrintStream provides two other additional methods which are helpful in displaying information, which are the print and printf methods. 

The print method has the same overloads as the println method, and similarly, the same types can be logged. The difference however is that while println automatically prints a newline, the print method doesn’t. This means that subsequent invocations of the print method will all end up on the same line, as can be seen below:

System.out.print("Hello ");
System.out.print("World");

Will result in the text “Hello World” being printed.

The printf method is slightly more advanced, and allows us to write a formatted String to the standard output stream. To format the String, the Java Format String Syntax can be used. An example of the printf function can be seen below:

Calendar c = Calendar.getInstance();
System.out.printf("The interest rate for '%1s' at %2$te-%2$tm-%2$tY is %3$.2f%%", "saving accounts", c, 3.512);

When we run this code, it will produce the following output:

The interest rate for 'saving accounts' at 23-06-2019 is 3.51%

While System.out provides us with a lot of functionality, they are not always the most flexible to use. It’s for example not easily possible to make a distinction between and error message or an information message, or to selectively turn messages on or off. For this, we use loggers.

Loggers

Similar to System.out.println, loggers capture messages or events. While System.out directly forwards those messages to the Printstream, Loggers pass those messages to the appropriate appenders. There are 3 well known logging systems in the Java eco system, which are JUL (Java Util Logging), Log4j and Logback

Loggers consist of several components, such as Log Levels, Logging methods, handlers, formatters and configuration. Each of these elements are implemented ion a slightly different way for each of the logging frameworks. To overcome this, we could use the SLF4J logging facade, which will provide a common interface over most logging frameworks, but isn’t a logging framework itself. 

A typical use case for a logger would be to log a message at a certain level. For example, loggers are typically used to make the distinction between error messages and information messages, as can bee seen in the following Log4j example example:

import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
 
public class Log4jExample {
 
    private static Logger logger = LogManager.getLogger(Log4jExample.class);
 
    public static void main(String[] args) {
        logger.info("Info log message");
        logger.error("Error log message");
    }
}

As can be seen in the code above, it requires slightly more code to use a logger. Additionally, an extra dependency is needed on a logging framework. While this is often not a big issue, it  will increase the size of your application slightly. However, by using a logging framework, it does overcome some of the limitations of System.out, such as:

  • more easily turn log messages on/off, based on configuration.
  • more easily append log messages to files, purge messages, use rolling appenders, etc, by using out of the box components.

Conclusion

In this article we’ve given an overview of some of the differences and similarities between System.out.println and commonly used loggers. While System.out.println can be effectively used to debug an application and provide feedback to a programmer, having a more flexible system in place is often desired to be able to filter and redirect log messages to the appropriate systems. 

Leave a Reply

Your email address will not be published. Required fields are marked *