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 injava.lang
package.out
is a static member field ofSystem
class and is of typePrintStream
. 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 ofPrintStream
class.println
prints the argument passed to the standard console and a newline. There are multipleprintln
methods with different arguments. Every println makes a call toprint
method and adds a newline. Internally,print
callswrite()
and the story goes on like that.
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,
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,
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 thanDEBUG
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 usingPatternLayout
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!
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
.
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.
The project named SysOutToLog4j
will be created. Let’s create the required java files. Right click on src
folder, New -> Package
.
A new pop window will open where we will enter the package name as: com.sysOut.to.Log4j
and click Finish.
Repeat the step (i.e. Fig. 7) and enter the package name as: com.sysOut.Implementation.Test
and click Finish.
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
.
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
.
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
.
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
.
A new pop window will open and select the wizard as File
.
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.
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
.
5. Project Demo
When we will execute the example the output will be displayed on the console and printed in the log file.
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.
You can download the full source code of this example here: SysOutToLog4j
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…
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