Apache logging services logo Apache log4j logo

Custom Log Levels

Log4J 2 supports custom log levels. The Level.forName() method creates a new level for the specified name. By calling the Logger.log() method and passing this custom log level you can log messages at this level:

final Logger logger = LogManager.getLogger();
final Level VERBOSE = Level.forName("VERBOSE", 550);

logger.log(VERBOSE, "a verbose message");
logger.log(VERBOSE, "another message");

When defining a custom log level, the intLevel parameter (550 in the example above) determines where the custom level exists in relation to the standard levels built-in to Log4J 2. For reference, the table below shows the intLevel of the built-in log levels.

Standard log levels built-in to Log4J
Standard Level intLevel
OFF 0
FATAL 100
ERROR 200
WARN 300
INFO 400
DEBUG 500
TRACE 600
ALL Integer.MAX_VALUE

Convenience Methods for the Built-in Log Levels

The built-in log levels have a set of convenience methods on the Logger interface that makes them easier to use. For example, the Logger interface has fourteen debug() methods that support the DEBUG level:

// convenience methods for the built-in DEBUG level
debug(Marker, Message)
debug(Marker, Message, Throwable)
debug(Marker, Object)
debug(Marker, Object, Throwable)
debug(Marker, String)
debug(Marker, String, Object...)
debug(Marker, String, Throwable)
debug(Message)
debug(Message, Throwable)
debug(Object)
debug(Object, Throwable)
debug(String)
debug(String, Object...)
debug(String, Throwable)

Similar methods exist for the other built-in levels.

It would be nice to have the same ease of use with custom levels, so that after declaring a custom VERBOSE level, we could use code like this:

logger.verbose("a verbose message"); // no need to pass the VERBOSE level as a parameter
logger.verbose("another message");

Adding or Replacing Log Levels

In the above example, a convenience method was added to the Logger interface, in addition to the existing trace(), debug(), info(), ... methods for the built-in log levels.

There is another use case, Domain Specific Language loggers, where we want to replace the existing trace(), debug(), info(), ... methods with all-custom methods.

For example, for medical devices we could have only critical(), warning(), and advisory() methods. Another example could be a game that has only defcon1(), defcon2(), and defcon3() levels.

If it were possible to hide existing log levels, users could customize the Logger interface to match their requirements. Some people may not want to have a FATAL or a TRACE level, for example. They would like to be able to create a custom Logger that only has debug(), info(), warn() and error() methods.

Generating Source Code for a Custom Logger Wrapper

Common Log4J usage is to get an instance of the Logger interface from the LogManager and call the methods on this interface. However, the custom log Levels are not known in advance, so Log4J cannot provide an interface with convenience methods for these custom log Levels.

To solve this, Log4J ships with a tool that generates source code for a Logger wrapper. The generated wrapper class has convenience methods for each custom log level, making custom levels just as easy to use as the built-in levels.

There are two flavors of wrappers: ones that extend the Logger API (adding methods to the built-in levels) and ones that customize the Logger API (replacing the built-in methods).

When generating the source code for a wrapper class, you need to specify:

  • the fully qualified name of the class to generate
  • the list of custom levels to support and their intLevel relative strength
  • whether to extend Logger (and keep the existing built-in methods) or have only methods for the custom log levels

You would then include the generated source code in the project where you want to use custom log levels.

Example Usage of a Generated Logger Wrapper

Here is an example of how one would use a generated logger wrapper with custom levels DIAG, NOTICE and VERBOSE:

// ExtLogger is a generated logger wrapper
import com.mycompany.myproject.ExtLogger;

public class MyService {
    // instead of Logger logger = LogManager.getLogger(MyService.class):
    private static final ExtLogger logger = ExtLogger.create(MyService.class);
    
    public void someMethod() {
        // ...
        logger.trace("the built-in TRACE level");
        logger.verbose("a custom level: a VERBOSE message");
        logger.debug("the built-in DEBUG level");
        logger.notice("a custom level: a NOTICE message");
        logger.info("the built-in INFO level");
        logger.diag("a custom level: a DIAG message");
        logger.warn("the built-in WARN level");
        logger.error("the built-in ERROR level");
        logger.fatal("the built-in FATAL level");
        // ...
    }
    ...
}

Generating Extended Loggers

Use the following command to generate a logger wrapper that adds methods to the built-in ones:

java -cp log4j-core-2.0.2.jar org.apache.logging.log4j.core.tools.Generate$ExtendedLogger \
        com.mycomp.ExtLogger DIAG=350 NOTICE=450 VERBOSE=550 > com/mycomp/ExtLogger.java

This will generate source code for a logger wrapper that has the convenience methods for the built-in levels as well as the specified custom levels. The tool prints the generated source code to the console. By appending " > filename" the output can be redirected to a file.

Generating Custom Loggers

Use the following command to generate a logger wrapper that hides the built-in levels and has only custom levels:

java -cp log4j-core-2.0.2.jar org.apache.logging.log4j.core.tools.Generate$CustomLogger \
        com.mycomp.MyLogger DEFCON1=350 DEFCON2=450 DEFCON3=550 > com/mycomp/MyLogger.java

This will generate source code for a logger wrapper that only has convenience methods for the specified custom levels, not for the built-in levels. The tool prints the generated source code to the console. By appending " > filename" the output can be redirected to a file.