Logging System.out.println Results in a Log File Example (original) (raw)

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.

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:

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(),

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

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