Skip to main content

How To Use Lazy Loading for Efficient Java Logging

It is always a good Java programming practice to prefix log statements with a check for "loggability" by testing Logger.isLoggable(Level) prior to logging. However, this practice presents Java developers with a dilemma: Failing to do so results in slower performance, but following it contributes to more total lines of code. Often, developers opt to just bypass it.

In this intermediate Java tip, Jason Weinstein offers a solution: lazy loading. By wrapping Java logging in your own class (as shown below), you can ensure the isLoggable() test is always made, while at the same time lazily binding the message creation so that it occurs only when the message is going to be logged.


Logger logger = Logger.getLogger("your class name");

Here's an example of bad logging practice:
logger.log(Level.FINE, "your message " + foo + " " + bar);
Note the string concatenation and toString() calls made regardless of whether the message is logged or not.
Here's an example of good logging practice:
if (logger.isLoggable(Level.FINE)) {
    logger.log(Level.FINE, "your message " + foo + " " + bar);
}

Here's an example of how lazy logging can simplify the good practice above:
LazyLogger logger = LazyLogger.getLogger("your class name");

// avoids string concatenation and toString() method calls

    logger.log(Level.FINE, "your message ", foo, " ", bar);

Here's a full example class:

public class LazyLogger {
    private final Logger logger;

    public LazyLogger(Logger logger) {
        if (logger == null) {
            throw new IllegalArgumentException("logger cannot be null");
        }
        this.logger = logger;
    }

    public static LazyLogger getLogger(String name) {
        Logger logger = Logger.getLogger(name);
        return new LazyLogger(logger);
    }

    public void finest(Object... params) {
        log(Level.FINEST, params);
    }

    public void finer(Object... params) {
        log(Level.FINER, params);
    }

    public void fine(Object... params) {
        log(Level.FINE, params);
    }

    public void config(Object... params) {
        log(Level.CONFIG, params);
    }

    public void info(Object... params) {
        log(Level.INFO, params);
    }

    public void warning(Object... params) {
        log(Level.WARNING, params);
    }

    public void severe(Object... params) {
        log(Level.SEVERE, params);
    }

    public void log(Level level, Object... params) {
        // Check logging level before continuing
        if (logger.isLoggable(level)) {
            String message;
            if (params != null) {
                if (params.length == 1) {
                    message = String.valueOf(params[0]);
                } else {
                    StringBuilder buf = new StringBuilder();
                    for (Object param : params) {
                        buf.append(param);
                    }
                    message = buf.toString();
                }
            } else {
                message = "null";
            }
            logger.log(level, message);
        }
    }

    public static void main(String[] args) {
        LazyLogger logger = LazyLogger.getLogger("main");
        logger.log(Level.FINE, "Hello", " ", "World");
    }

Some More Tips by Experts on above article:
1. Be aware that the variable parameter list do not perform like a fix variable list.String's operator in modern JVM is highly optimized. The problem is not the concatenation of strings, The problem lies in foo & bar: the cost to get them (maybe need a call to a database) and to hold them (very long string representation). But then, foo & bar should not be logged.

2. More specifically, the problem isn't cat-ing Strings, it's garbage-collecting strings. Fewer strings helps, so this solution makes things better. Constant strings won't be garbage-collected. In practice, you'll also want methods that take Throwable as an argument. What would that make the varg-based API look like?

As a side note, instead of just declaring that the best-practice is to use an if-check on the logging level, you should discuss the problems with that approach. I've run into a lot of merge issues, especially with ClearCase. Critical logic gets merged into the if block. Something is broken, but turning logging on magically fixes the problem. Much aggravation follows. For some projects, we decided the best practice was to avoid the if's until they've proven to be a problem.

3.If I have to log big and different messages (real-time DB error message, or dynamic xml information, etc..) then in lazy logging the independent strings irrespective of the logging category under which the message is logged have to be built.

Ex:- The method call:
logger.log(Level.INFO, "Message : " myObj.toString());

builds the myObj string even if the logger level is sent to ERROR or FATAL level.
In case of services built in Java, issue becomes more prevalent as we need to judicially build the strings when they are needed because of the Java heap limits.


Thanks to Jason

 

Comments

  1. Great article , you have indeed covered topic in details with code examples and explanation. I have also blogged some of my experience as 10 tips on logging in Java

    Thanks
    Javin
    10 tips on logging in Java

    ReplyDelete

Post a Comment

Popular posts from this blog

Java Health Center - Overview and Features

This video highlights the features available in Health Center. Health Center is a low-processor and memory usage diagnostic tool for monitoring the status of a running IBM Java Virtual Machine (JVM). Health Center provides live information and recommendations in each of the following areas: - Classes: information about classes being loaded - Environment: details of the configuration and system of the monitored application - Garbage Collection: information about the Java heap and pause times - Locking: information about contention on inflated locks - Profiling: sampling profile of Java methods including call paths

WebSphere MQ Interview Questions

What is MQ and what does it do? Ans. MQ stands for MESSAGE QUEUEING. WebSphere MQ allows application programs to use message queuing to participate in message-driven processing. Application programs can communicate across different platforms by using the appropriate message queuing software products. What is Message driven process? Ans . When messages arrive on a queue, they can automatically start an application using triggering. If necessary, the applications can be stopped when the message (or messages) have been processed. What are advantages of the MQ? Ans. 1. Integration. 2. Asynchrony 3. Assured Delivery 4. Scalability. How does it support the Integration? Ans. Because the MQ is independent of the Operating System you use i.e. it may be Windows, Solaris,AIX.It is independent of the protocol (i.e. TCP/IP, LU6.2, SNA, NetBIOS, UDP).It is not required that both the sender and receiver should be running on the same platform What is Asynchrony? Ans. With messag...

Java Garbage Collection

Explain Garbage collection mechanism in Java? Garbage collection is one of the most important features of Java. The purpose of garbage collection is to identify and discard objects that are no longer needed by a program so that their resources can be reclaimed and reused. A Java object is subject to garbage collection when it becomes unreachable to the program in which it is used. Garbage collection is also called automatic memory management as JVM automatically removes the unused variables/objects (value is null) from the memory. Every class inherits finalize() method from java.lang.Object, the finalize() method is called by garbage collector when it determines no more references to the object exists. In Java, it is good idea to explicitly assign null into a variable when no more in use. In Java on calling System.gc() and Runtime.gc(), JVM tries to recycle the unused objects, but there is no guarantee when all the objects will garbage collected. Garbage collection is an automatic pro...