Levels

Log levels are used to categorize log events by severity and control the verbosity of the logs. They are one of many fish tagging capabilities provided by Log4j API. Using levels, you can filter out less important logs and focus on the most critical ones.

Log4j contains following predefined levels:

Table 1. Standard log levels
Name Priority

OFF[see note]

0

FATAL

100

ERROR

200

WARN

300

INFO

400

DEBUG

500

TRACE

600

ALL[see note]

Integer.MAX_VALUE

The OFF and ALL levels are special: they should not be used to fish-tag log events.

Log4j API implementations, such as Log4j Core, can use OFF in their configuration files to disable all log statements and ALL to enabled them all.

A level is composed of a case-sensitive name and a priority (of type int), which is used to define an order while comparing two. Priority can be used in several contexts to express a filtering capability, for instance:

  • WARN is less severe than ERROR

  • WARN is less specific than ERROR

The entry point to log levels are through Level. Predefined levels are available for Log4j API integrators through StandardLevel.

Usage

To assign a level to a log event you can use one of the variants of the Logger.log(..) and Logger.atLevel(Level) methods:

LOGGER.log(Level.INFO, "Hello {}!", username);
LOGGER.atLevel(Level.INFO).log("Hello {}!", username);

The Logger interface also contains shorthand methods that always log at a specified log level:

Table 2. Shorthand Logger methods
Effective level Shorthand methods

FATAL

Logger.fatal(..), Logger.atFatal()

ERROR

Logger.error(..), Logger.atError()

WARN

Logger.warn(..), Logger.atWarn()

INFO

Logger.info(..), Logger.atInfo()

DEBUG

Logger.debug(..), Logger.atDebug()

TRACE

Logger.trace(..), Logger.atTrace()

By using shorthand methods, you can rewrite the example above as:

LOGGER.info("Hello {}!", username);
LOGGER.atInfo().log("Hello {}!", username);

Which level to use?

While Log4j API defines a set of standard levels, it does not define the purpose of these levels. Many different conventions on which log levels to use coexist in the industry. When in doubt, you should ask your teammates about the convention used at your company.

Most log level usage conventions divide log levels into two categories:

  • the most severe log levels (e.g. FATAL, ERROR and WARN) are used to inform the system administrator about a problem in the Java application that needs to be fixed. The more severe the problem, the more severe the log level.

    Log events with these levels should be used sparingly and should allow the system administrator to fix the problem.

  • the less severe log levels (e.g. INFO, DEBUG, TRACE) provide context that allow a system administrator or developer to diagnose the reason of an application failure. The most severe of them describe events that concern the whole application, while the less severe describe events that are interesting for a single sub-system.

Custom log levels

While most Java logging APIs adopt the same set of standard logging levels, some logging APIs, such as JUL and external logging systems, such as Syslog and OpenTelemetry support additional logging levels that can not be mapped to the standard ones.

To improve interoperability between logging systems, Log4j API supports custom log levels that can be defined using the Level.forName() method:

// OpenTelemetry additional INFO levels
private static final Level INFO2 = Level.forName("INFO2", 375);
private static final Level INFO3 = Level.forName("INFO3", 350);
private static final Level INFO4 = Level.forName("INFO4", 325);

Custom log levels can be used in your code with the usual Logger.log(..) and Logger.atLevel(Level) methods:

LOGGER.log(INFO2, "Hello {}!", username);
LOGGER.atLevel(INFO3).log("Hello {}!", username);

Implementation support

All logging implementations support filtering of log events, based on their log level, but the number of available log levels varies between implementations.

While most logging implementations support standard log levels, custom log levels are only supported by Log4j Core (and the EOL Log4j 1). To ensure independence from a specific logging implementation you should restrict your log statements to standard log levels.

If you use custom log levels as a fish-tagging technique, you can use alternative fish-tagging features such as Markers, which are supported by multiple logging implementations.

Log4j Core

The Log4j Core implementation fully supports both standard and custom levels. Similarly to the Log4j API usage, custom levels must be defined in a configuration file before they can be used. You can do it using CustomLevel configuration elements:

  • XML

  • JSON

  • YAML

  • Properties

Snippet from an example log4j2.xml
<Appenders>
  <Console name="CONSOLE">
    <PatternLayout pattern="%d [%t] %p %c - %m%n"/>(1)
  </Console>
</Appenders>
<CustomLevels>(4)
  <CustomLevel name="INFO2" intLevel="375"/>
  <CustomLevel name="INFO3" intLevel="350"/>
  <CustomLevel name="INFO4" intLevel="325"/>
</CustomLevels>
<Loggers>
  <Logger name="com.example" level="DEBUG"/>(2)
  <Root level="INFO2">(5)
    <AppenderRef ref="CONSOLE" level="WARN"/>(3)
  </Root>
</Loggers>
Snippet from an example log4j2.json
"Appenders": {
  "Console": {
    "name": "CONSOLE",
    "PatternLayout": {
      "pattern": "%d [%t] %p %c - %m%n" (1)
    }
  }
},
"CustomLevels": { (4)
  "CustomLevel": [
    {
      "name": "INFO2",
      "intLevel": 375
    },
    {
      "name": "INFO3",
      "intLevel": 350
    },
    {
      "name": "INFO4",
      "intLevel": 325
    }
  ]
},
"Loggers": {
  "Logger": {
    "name": "com.example",
    "level": "DEBUG" (2)
  },
  "Root": {
    "level": "INFO2", (5)
    "AppenderRef": {
      "ref": "CONSOLE",
      "level": "WARN" (3)
    }
  }
}
Snippet from an example log4j2.yaml
Appenders:
  Console:
    name: "CONSOLE"
    PatternLayout:
      pattern: "%d [%t] %p %c - %m%n" (1)
CustomLevels: (4)
  CustomLevel:
    - name: "INFO2"
      intLevel: 375
    - name: "INFO3"
      intlevel: 350
    - name: "INFO4"
      intLevel: 325
Loggers:
  Logger:
    name: "com.example"
    level: "DEBUG" (2)
  Root:
    level: "INFO2" (5)
    AppenderRef:
      ref: "CONSOLE"
      level: "WARN" (3)
Snippet from an example log4j2.properties
appender.0.type = Console
appender.0.name = CONSOLE
appender.0.layout.type = PatternLayout
(1)
appender.0.layout.pattern = %d [%t] %p %c - %m%n

(4)
customLevel.INFO2 = 375
customLevel.INFO3 = 350
customLevel.INFO4 = 325

logger.0.name = com.example
(2)
logger.0.level = DEBUG
(5)
rootLogger.level = INFO2
rootLogger.appenderRef.0.ref = CONSOLE
(3)
rootLogger.appenderRef.0.level = WARN
1 All the available Layouts support printing levels. In the case of Pattern Layout you can use a %p or %level pattern.
2 Loggers support a level configuration attribute to filter log events.
3 A level attribute is also available in appender references.
4 Custom levels must be defined before they can be used.
5 Custom levels can be used anywhere a standard level can be used.

SLF4J implementations (Logback)

Since SLF4J only supports five log levels (ERROR, WARN, INFO, DEBUG and TRACE) and does not support custom log levels, Log4j API levels are converted according to the following table:

Table 3. Log4j to SLF4J level conversion
Log4j level priority Log4j standard levels SLF4J Level

0 < priority < 300

FATAL, ERROR

ERROR

300 ≤ priority < 400

WARN

WARN

400 ≤ priority < 500

INFO

INFO

500 ≤ priority < 600

DEBUG

DEBUG

600 ≤ priority

TRACE

TRACE

JUL (java.util.logging)

Similarly to Log4j API, java.util.logging also supports custom log levels, but the current Log4j-to-JUL bridge implementation does not take advantage of them. The conversion of between Log4j log levels and JUL levels is performed accordingly to the following table:

Table 4. Log4j to Java level conversion
Log4j level priority Log4j standard levels Java Level

0 ≤ priority < 300

FATAL, ERROR

SEVERE

300 ≤ priority < 400

WARN

WARNING

400 ≤ priority < 500

INFO

INFO

500 ≤ priority < 600

DEBUG

FINE

600 ≤ priority

TRACE

FINER