Log4j

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:

DescriptionMethod 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:

Fig. 1: Log4j2 Appenders
Fig. 1: Log4j2 Appenders

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:

Fig. 2: Log4j2 Layout
Fig. 2: Log4j2 Layout

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 than DEBUG 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 using PatternLayout 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 static Logger 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 for log4j2.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. The 5 in the %5p is to set the width of the field to the 5 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.

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.

6 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Ferda Mravenec
Ferda Mravenec
7 years ago

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

Nick
Nick
7 years ago

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.

jool
jool
6 years ago

The code example that follows the introduction “Always log the decision making statements” has an unmatched double quote which makes the example sub-optimal. :-)

Akshay Goel
Akshay Goel
6 years ago

You have pointed out that there are 5 different logging methods methods but you have listed out 6 yourself.Can you please explain this.

Back to top button