Out

Logging System.out.println Results in a Log File Example

In Java the three main streams stdin (Standard Input), stdout (Standard Output), and stderr (Standard Output Error) are handled by default by System.in, Sytem.out, and System.err respectively.

In this example, we will try to show how to redirect the System.out.println() to a log file using Log4j logging services.

1. Introduction

Printing messages to the console is an integral part of development, testing and debugging a Java program. If developers are working on a Server side application, where they cannot see what’s going on inside the server, then their only visibility tool is a log file.

Without logs, developers cannot do any debugging or see what’s going on inside the application. Though, Java has pretty handy System.out.println() methods to print something on console, which can also be routed to log file but not sufficient for a real-world Java application.

If developers are running a Java program in Linux or UNIX based systems, Log4j or SLF4j or any other logging framework offers a lot more features, flexibility, and improvement on message quality, which is not possible using the System.out.println() statements.

1.1 What is System.out.println?

System.out.println is a Java statement that prints the argument passed, into the System.out which is generally stdout.

  • System is a final class built into the core Java language in java.lang package.
  • out is a static member field of System class and is of type PrintStream. Its access specifiers are public final. This gets instantiated during startup and gets mapped with standard output console of the host. This stream is open by itself immediately after its instantiation and ready to accept data.
  • println is an overloaded method of PrintStream class. println prints the argument passed to the standard console and a newline. There are multiple println methods with different arguments. Every println makes a call to print method and adds a newline. Internally, print calls write() and the story goes on like that.

Fig. 1: System.out.println
Fig. 1: System.out.println

Now, you might be thinking that can we create an object of PrintStream and call println function with that object to print to the standard output (usually the console)? The answer is NO. When you want to print to the standard output, then you will use System.out. Instantiating a PrintStream will allow you to write to a File or OutputStream you specify, but don’t have anything to do with the console.

However, you can pass System.out to PrintStream and then invoke println on PrintStream object to print to the standard output. Following is a small example:

SystemOutPrintlnDemo.java

import java.io.PrintStream;

public class SystemOutPrintlnDemo {

  public static void main(String[] args) {
    
    // Creating PrintStream Object
    PrintStream psObj = new PrintStream(System.out);
    psObj.println("Hello World!");
    psObj.print("Hello World Again!");
    
    // Flushing the Stream
    psObj.flush();
  }
}

1.1.1 System.out.println & Performance

There is a general notion that System.out.println are bad for performance. When we analyze deeply, the sequence of calls are like println -> print -> write() + newLine(). This sequence flow is an implementation of Sun/Oracle JDK. Both write() and newLine() contains a synchronized block. Synchronization has a little overhead, but more than that the cost of adding characters to the buffer and printing is high.

When we run a performance analysis, run multiple numbers of System.out.println and record the time, the execution duration increases proportionally. Performance degrades when we print more than 50 characters and print more than 50,000 lines.

It all depends on the scenario we use it. Whatever may be the case, do not use System.out.println for logging to stdout.

1.2 What is Log4j?

Log4j is a simple, flexible, and fast Java based logging framework. It is Thread safe and supports internationalization. We mainly have 3 components to work with Log4j:

  • Logger: It is used to log the messages.
  • Appender: It is used to publish the logging information to the destination like a file, database, console etc.
  • Layout: It is used to format logging information in different styles.

1.2.1 Logger Class

Logger class provides the methods for logging process. We can use the getLogger() method to get the Logger object. The syntax is given below:

Getting Logger Object

static Logger log = Logger.getLogger(YourClassName.class);

Logger class has 5 logging methods which are used to print the status of an application,

Description Method Syntax
debug(Object message) It is used to print the message with the level org.apache.log4j.Level.DEBUG. public void debug(Object message)
error(Object message) It is used to print the message with the level org.apache.log4j.Level.ERROR. public void error(Object message)
info(Object message) It is used to print the message with the level org.apache.log4j.Level.INFO. public void info(Object message)
fatal(Object message) It is used to print the message with the level org.apache.log4j.Level.FATAL. public void fatal(Object message)
warn(Object message) It is used to print the message with the level org.apache.log4j.Level.WARN. public void warn(Object message)
trace(Object message) It is used to print the message with the level org.apache.log4j.Level.TRACE. public void trace(Object message)

 

To summarize, the priority level is given below.

Trace < Debug < Info < Warn < Error < Fatal

Where org.apache.log4j.Level.FATAL has the highest priority and org.apache.log4j.Level.Trace as the lowest.

1.2.2 Appender

Appender is an interface which is primarily responsible for printing the logging messages to the different destinations such as console, files, sockets, database etc. In Log4j we have different types of Appender implementation classes,

Fig. 2: Log4j Appenders
Fig. 2: Log4j Appenders

1.2.3 Layout

Layout component specifies the format in which the log statements are written into the destination repository by the Appender. In Log4j we have different types of Layout implementation classes,

Fig. 3: Log4j Layout
Fig. 3: Log4j Layout

1.3 Why prefer Log4j over System.out.println?

Below are some of the reasons, which are enough to understand the limitation of using System.out.println(),

  • Any logging framework including allows developers to log debugging information with a log level which can be used as filtering criteria, i.e. one can disable the message belongs to a particular log level. For e.g., Developers would be more concerned to see the WARN messages than DEBUG messages in the production environment.
  • Logging framework can produce better outputs and meta data which help to troubleshoot and debug. For e.g., Log4j allows to print formatted output by specifying a formatting pattern i.e. by using PatternLayout one can include a timestamp, class name etc.

Now, open up the Eclipse IDE and let’s start building the application!

2. Logging System.out.println Results in a Log File

Below are the steps involved in developing this application.

2.1 Tools Used

We are using Eclipse Kepler SR2, JDK 8, and Log4j Jar. Having said that, we have tested the code against JDK 1.7 and it works well.

2.2 Project Structure

Firstly, let’s review the final project structure, in case you are confused about where you should create the corresponding files or folder later!

Fig. 4: Application Project Structure
Fig. 4: Application Project Structure

2.3 Project Creation

This section will demonstrate you how to create a Java project with Eclipse. In Eclipse IDE, go to File -> New -> Java Project.

Fig. 5: Create Java Project
Fig. 5: Create Java Project

In the New Java Project window, it will ask you to enter the project name and select project location. By default, ‘Use default workspace location‘ will be selected. Select the ‘Use default JRE‘ radio button and click Finish.

Fig. 6: Project Details
Fig. 6: Project Details

The project named SysOutToLog4j will be created. Let’s create the required java files. Right click on src folder, New -> Package.

Fig. 7: Java Package Creation
Fig. 7: Java Package Creation

A new pop window will open where we will enter the package name as: com.sysOut.to.Log4j and click Finish.

Fig. 8: Java Package Name (com.sysOut.to.Log4j)
Fig. 8: Java Package Name (com.sysOut.to.Log4j)

Repeat the step (i.e. Fig. 7) and enter the package name as: com.sysOut.Implementation.Test and click Finish.

Fig. 9: Java Package Name (com.sysOut.Implementation.Test)
Fig. 9: Java Package Name (com.sysOut.Implementation.Test)

Once the package is created in the application, we will need to create the required classes. Right click on the newly created package, New -> Class.

Fig. 10: Java Class Creation
Fig. 10: Java Class Creation

A new pop window will open and enter the file name as SystemOutToLog4j. The logging service class will be created inside the package: com.sysOut.to.Log4j.

Fig. 11: Java Class (SystemOutToLog4j.java)
Fig. 11: Java Class (SystemOutToLog4j.java)

Repeat the step (i.e. Fig. 10) and enter the filename as TestSysOutToLog4j. The implementation class will be created inside the package: com.sysOut.Implementation.Test.

Fig. 12: Java Class (TestSysOutToLog4j.java)
Fig. 12: Java Class (TestSysOutToLog4j.java)

2.3.1 Implementation of Logging Service

This class is used to redirect the println messages to the logger. Add the following code to it:

SystemOutToLog4j.java

package com.sysOut.to.Log4j;

import java.io.PrintStream;

public class SystemOutToLog4j extends PrintStream {

	private static final PrintStream originalSystemOut = System.out;
	private static SystemOutToLog4j systemOutToLogger;	

	@SuppressWarnings("rawtypes")
	public static void enableForClass(Class className) {
		systemOutToLogger = new SystemOutToLog4j(originalSystemOut, className.getName());
		System.setOut(systemOutToLogger);
	}

	public static void enableForPackage(String packageToLog) {
		systemOutToLogger = new SystemOutToLog4j(originalSystemOut, packageToLog);
		System.setOut(systemOutToLogger);
	}

	public static void disable() {
		System.setOut(originalSystemOut);
		systemOutToLogger = null;
	}

	private String packageOrClassToLog;
	private SystemOutToLog4j(PrintStream original, String packageOrClassToLog) {
		super(original);
		this.packageOrClassToLog = packageOrClassToLog;
	}
	
	@Override	
	public void println(String line) {
		StackTraceElement[] stack = Thread.currentThread().getStackTrace();
		StackTraceElement caller = findCallerToLog(stack);
		if (caller == null) {
			super.println(line);
			return;
		}

		org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(caller.getClass());
		logger.debug("Code Line No.: " + stack[2].getLineNumber() + ", Class Name: " + caller.getClassName() + ", Text: " + line);
	}

	public StackTraceElement findCallerToLog(StackTraceElement[] stack) {
		for (StackTraceElement element : stack) {
			if (element.getClassName().startsWith(packageOrClassToLog)) {
				return element;
			}			
		}
		return null;
	}
}

2.3.2 Implementation of Main Class

This class is used to enable the println logging to a log file. We can enable the Log4j in this way:

SystemOutToLog4j.enableForClass(YourClassName.class);

After this, all the line printed to the standard output (stdout) will be redirected. Add the following code to it:

TestSysOutToLog4j.java

package com.sysOut.Implementation.Test;

import org.apache.log4j.Logger;
import com.sysOut.to.Log4j.SystemOutToLog4j;

public class TestSysOutToLog4J {
	 
	final static Logger logger = Logger.getLogger(TestSysOutToLog4J.class.getName());
	
	static {
		SystemOutToLog4j.enableForClass(TestSysOutToLog4J.class);
	}
	
	public static void main(String[] args) {
		logger.debug("Hello this is a debug message");
		System.out.println("Print In Log File");
		logger.info("Hello this is a info message");
	}
}

3. Log4j Configuration File

Log4j will be usually configured using a properties file or XML file. So once the log statements are in place developers can easily control them using the external configuration file without modifying the source code.

The log4j.properties file is a Log4j configuration file which keeps properties in key-value pairs. By default, the LogManager looks for a file named log4j.properties in the CLASSPATH.

To configure the logging framework, we need to implement a configuration file i.e. log4j.properties. Right click on src folder, New -> Other.

Fig. 13: File Creation
Fig. 13: File Creation

A new pop window will open and select the wizard as File.

Fig. 14: Wizard Creation
Fig. 14: Wizard Creation

Again, a pop-up window will open. Verify the parent folder location as SysOutToLog4j/src and enter the file name as log4j.properties. Click Finish.

Fig. 15: log4j.properties
Fig. 15: log4j.properties

Once the file is created, add the following code to it:

log4j.properties

#Log File Location !!
logFileLoc = ../SysOutToLog4j/logs/project/

# Root Location Option !!
log4j.rootLogger=DEBUG, consoleAppender, fileAppender

# Redirect Log Messages To Console !!
log4j.appender.consoleAppender=org.apache.log4j.ConsoleAppender
log4j.appender.consoleAppender.Target=System.out
log4j.appender.consoleAppender.layout=org.apache.log4j.PatternLayout
log4j.appender.consoleAppender.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n

# Redirect Log Messages To A Debug Log File, Support File Rolling !!
log4j.appender.fileAppender=org.apache.log4j.RollingFileAppender
log4j.appender.fileAppender.File=${logFileLoc}/debug-log.out
log4j.appender.fileAppender.MaxFileSize=5MB
log4j.appender.fileAppender.MaxBackupIndex=10
log4j.appender.fileAppender.Append=true
log4j.appender.fileAppender.layout=org.apache.log4j.PatternLayout
log4j.appender.fileAppender.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n

4. Run the Application

To run the application, Right click on the TestSysOutToLog4J class, Run As -> Java Application.

Fig. 16: Run Application
Fig. 16: Run Application

5. Project Demo

When we will execute the example the output will be displayed on the console and printed in the log file.

Fig. 17: Logging Output
Fig. 17: Logging Output

That’s all for this post. Happy Learning!!

6. Conclusion

Here in this example, we learned about the advantages of using Log4j over System.out.println() in a real time environment.

7. Download the Eclipse Project

This was an example of System.out Logging.

Download
You can download the full source code of this example here: SysOutToLog4j

Yatin

An experience full-stack engineer well versed with Core Java, Spring/Springboot, MVC, Security, AOP, Frontend (Angular & React), and cloud technologies (such as AWS, GCP, Jenkins, Docker, K8).
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Vitaly
Vitaly
5 years ago

Hello.
Nice example, but it could be better if you’ll show how to disable output of redirected log to the console. While debugging it creates a huge load if printed to the console at the real time thats why it should be redirected to file, but i can’t disable it printing to the console…

Predator
Predator
4 years ago
Reply to  Vitaly

A very late reply but you can remove these lines from the log4j.properties to disable printing it to the console.

# Redirect Log Messages To Console !!
log4j.appender.consoleAppender=org.apache.log4j.ConsoleAppender
log4j.appender.consoleAppender.Target=System.out
log4j.appender.consoleAppender.layout=org.apache.log4j.PatternLayout
log4j.appender.consoleAppender.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L – %m%n

Back to top button