Log4j 2 Best Practices Example
Logging is a critical feature of any application. In this tutorial, we will cover some Log4j2 best practices that can help developers get started and improve the logging with Log4j2.
1. Introduction
Printing messages to the console is an integral part of the development testing and the debugging of 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 Log4j2?
Log4j2 is the updated version of the popular and influential Log4j library, which is 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.1.1 Log4j2 Logger Class
Logger
class provides the methods for the logging process. We can use the LogManager.getLogger()
method to get the Logger
object. The syntax is given below:
static Logger log = LogManager.getLogger(YourClassName.class);
Logger
class has 6 different 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.logging.log4j.Level.DEBUG . It is the lowest restricted logging level. | public void debug(Object message) |
info(Object message) | It is used to print the message with the level org.apache.logging.log4j.Level.INFO . It is more restricted than the DEBUG logging level and developers should log messages which are for an informative purpose. | public void info(Object message) |
warn(Object message) | It is used to print the message with the level org.apache.logging.log4j.Level.WARN . It is more restricted than the INFO logging level and is used to log the warning sort of messages i.e. Connection lost between Client and Server, Database Connection lost etc. | public void warn(Object message) |
error(Object message) | It is used to print the message with the level org.apache.logging.log4j.Level.ERROR . It is more restricted than the WARN logging level and is used to log errors and exceptions. | public void error(Object message) |
fatal(Object message) | It is used to print the message with the level org.apache.logging.log4j.Level.FATAL . | public void fatal(Object message) |
trace(Object message) | It is used to print the message with the level org.apache.logging.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.logging.log4j.Level.FATAL
has the highest priority and org.apache.logging.log4j.Level.Trace
the lowest.
1.1.2 Log4j2 Appender Interface
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 Log4j2
we have different types of Appender
implementation classes:
1.1.3 Log4j Layout Class
Layout
component specifies the format in which the log statements are written into the destination repository by the Appender
. In Log4j2
we have different types of Layout
implementation classes:
1.2 Why prefer Log4j2 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 allows developers to log debugging information to 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 metadata which helps to troubleshoot and debug. For e.g.,
Log4j2
allows to print formatted output by specifying a formatting pattern i.e. by usingPatternLayout
one can include a timestamp, class name etc
2. Log4j2 Best Practices
- Using Static Modifier for
LogManager
Object: When developers declare any variable in the code, it comes with an overhead. Developers can overcome this overhead by declaring the staticLogger
reference as shown below.private static final Logger log = Logger.getLogger(YourClassName.class);
- Using
isDebugEnabled()
for putting the DEBUG log in Java as it will save a lot of String concatenation activity. Below is an example of Debug mode in Java.if(logger.isDebugEnabled()) { logger.debug("java logging level is DEBUG Enabled"); }
- Carefully choose which kind of message should go to each level of logging in Java. It becomes extremely important if developers are writing server application in Java and the only way to see what is happening is the Log4j2 logs. If developers log too much information, the application performance will be affected. At the same time if developers don’t log important information like the incoming messages or the outgoing messages in Java logs, then it would become extremely difficult to identify the root cause of the issue
- Using either Log4j2 or
java.util.logging
for setting up the logging framework in Java. As a developer, I would recommend using the Log4j2 because it is very flexible. It allows changing the logging level in Java without restarting the application. To do this, developers can have Log4j2 Watchdog which continuously looks forlog4j2.xml
in a particular directory. If found, it loads it and reset the logging framework in Java - By using the
log4j2.xml
, developers can have different Logger configuration for the different Java classes. Developers can have some classes in INFO mode, some in WARN mode or ERROR mode - Another important point to remember is the format of Java logging. Log4j2 logger allows the developer to include the Thread Name and the fully qualified Java Class Name while printing logs. It would be impossible to find sequence of events if the application code is executed by multiple threads without having a thread name on it
- Developers can modify the configuration file in order to change the Pattern Layouts format for the fields which developers are throwing as output. Below is an example of a sample configuration file:
# Define the root logger with Appender APP log4j.rootLogger=DEBUG, stdout, APP # add a ConsoleAppender to the logger stdout to write to the console log4j.appender.stdout=org.apache.logging.log4j.core.appender.ConsoleAppender log4j.appender.stdout.layout=org.apache.logging.log4j.core.layout.PatternLayout # Pattern to output the caller's file name and line number. log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %d{yyyy-MM-dd HH:mm:ss.SSS}; - (%F:%L) - %m%n # Define the file for APP Appender log4j.appender.APP=org.apache.logging.log4j.core.appender.RollingFileAppender log4j.appender.APP.File=example.log #Define Max File Size for APP Appender log4j.appender.APP.MaxFileSize=100KB # Keep one backup file for APP Appender log4j.appender.APP.MaxBackupIndex=1 # Define the layout for APP Appender log4j.appender.APP.layout=org.apache.logging.log4j.core.layout.PatternLayout log4j.appender.APP.layout.ConversionPattern=%5p %t - %d{yyyy-MM-dd HH:mm:ss.SSS}; - %c [%thread] - %m%n
The following are the description of the Patterns appearing in the
log4j2.properties
file:%5p
: It writes the level in the log. The5
in the%5p
is to set the width of the field to the5
characters%d{yyyy-MM-dd HH:mm:ss.SSS}
: It writes the date in the given date-time format%t
: It writes the method name in the log%c
: It writes the absolute class name (For e.g.com.jcg.log4j2.demo
) in the log%m%n
: It writes the message to the log%L
: It writes the line number in the log%F
: It writes the class name in the log
- Make Customized Log4j2 Appenders: If developers want to do something that the standard Appenders do not support, they can either search online or write their own customized Appenders. For e.g. Developers can make their own custom Log4j2 Appender by extending the
AppenderSkeleton
class. It provides the code for a common functionality, such as support for threshold filtering and support for general filters. Developers can even add their functionality on top of it as shown below:import java.util.ArrayList; import java.util.List; import org.apache.log4j.AppenderSkeleton; import org.apache.log4j.spi.LoggingEvent; public class CustomAppender extends AppenderSkeleton { List eventsList = new ArrayList(); @Override protected void append(LoggingEvent event) { eventsList.add(event); } public void close() { } public boolean requiresLayout() { return false; } }
- While writing the message for logging in Java, try to use some kind of prefix to indicate which part of the application code is printing the log. For e.g. Client side, Database side or the Session side. Believe me, I have used this technique and it helped a lot while debugging the issues. For e.g. Developers can put all the database level log with a prefix “DB_LOG” and put all session level log with prefix “SESSION_LOG“
- If a given logger is not assigned with a level, then it inherits one from its closest ancestor. That’s why developers always assign the log level to a root logger in configuration file i.e.
log4j2.rootLogger=DEBUG
- Always log the decision making statements. For e.g. Developers have a Java application which loads some settings from the preference file or the environment. If it doesn’t found, then it loads the default settings and logs this information like below:
logger.info("Not able to load personal settings, Default Setting selected for user : {user});
- Be sure to use the proper logging levels within the application code. One of the big advantages of using a logging framework is being able to turn up or down the verbosity of the logging at any time. Don’t log everything as DEBUG. Be sure to think about what information will be helpful later when you are troubleshooting the application problems
- Developers can use filters which can be configured to suppress the specific log messages. The following is the configuration details for the
log4j2.properties
to set up the filters in order to suppress certain logging statements:# Only INFO log4j.appender.R=org.apache.logging.log4j.core.appender.RollingFileAppender log4j.appender.R.File=SampleLog.log log4j.appender.R.MaxFileSize=500KB log4j.appender.R.MaxBackupIndex=1 log4j.appender.R.layout=org.apache.logging.log4j.core.layout.PatternLayout log4j.appender.R.layout.ConversionPattern=%d [%t] %-5p %c - %m%n log4j.appender.R.filter.a=org.apache.log4j.varia.LevelRangeFilter log4j.appender.R.filter.a.LevelMin=INFO log4j.appender.R.filter.a.LevelMax=INFO # only ERROR log4j.appender.ERROR=org.apache.logging.log4j.core.appender.RollingFileAppender log4j.appender.ERROR.File=SampleLog.txt log4j.appender.ERROR.MaxFileSize=500KB log4j.appender.ERROR.MaxBackupIndex=1 log4j.appender.ERROR.layout=org.apache.logging.log4j.core.layout.PatternLayout log4j.appender.ERROR.layout.ConversionPattern=%d [%t] %-5p %c - %m%n log4j.appender.ERROR.filter.b=org.apache.log4j.varia.LevelMatchFilter log4j.appender.ERROR.filter.b.LevelToMatch=ERROR log4j.appender.ERROR.filter.b.AcceptOnMatch=true log4j.appender.ERROR.Threshold=ERROR
That’s all for this post. Happy Learning and don’t forget to share!!
3. Conclusion
These tips and examples on logging in Java are based on my experience and how I use the logging framework in Java. By no means, is not complete. I would love to hear some more tips from you guys and how you are using and customizing the Java logging.
Hi there.
Good example for beginners. But I disagree with this statement:
“Using either Log4j2 or java.util.logging for setting up the logging framework in Java.”
This is not best practise at all. The best practise would be to use logging facade. And this is what I miss the most – no words about logging facades.
Also as a developer I would be very tired to write this statement every single time I wnat to log something:
if(logger.isXxxEnabled()) {
logger.xxx(…);
}
Anyway, good startpoint ,)
Fedra, hope you are doing great and thank you for pointing out about the logging facade technique in the SLF4J framework. No doubt it’s really a great approach! :)
https://logging.apache.org/log4j/2.0/manual/api.html
logger.debug(“Logging in user {} with birthday {}”, user.getName(), user.getBirthdayCalendar());
With the code above the logging level will only be checked once and the String construction will only occur when debug logging is enabled.
The code example that follows the introduction “Always log the decision making statements” has an unmatched double quote which makes the example sub-optimal. :-)
You have pointed out that there are 5 different logging methods methods but you have listed out 6 yourself.Can you please explain this.
@Akshay – Hope you are doing well and I apologize for the typo error. I’ll check with the editors if I can get this correct!