'Hierarchy in log4j2 does not work as I expected

Below are a class and the log4j configuration:

$ vi My.java

public class My {
    private static final Logger logger = LogManager.getLogger("foo.bar"); 
      
    public static void main(String[] args) {
        logger.trace("My trace");
        logger.debug("My debug");
        logger.info("My info");
        logger.warn("My warn");
        logger.error("My error");
...

$ vi log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn">
    <Appenders>
        <Console name="console" target="system_out">
            <PatternLayout pattern="%d %-5p %c : %m%n" />
        </Console>
        <File name="foo" fileName="logs/foo.log">
            <PatternLayout pattern="%d %-5p %c : %m%n" />
        </File>
        <File name="foo.bar" fileName="logs/foo.bar.log">
            <PatternLayout pattern="%d %-5p %c : %m%n" />
        </File>
    </Appenders>
    <Loggers>
        <Root level="debug">
            <AppenderRef ref="console" />
        </Root>
        <Logger name="foo" level="info" additivity="false">
            <AppenderRef ref="foo" />
        </Logger>
        <Logger name="foo.bar" level="warn" additivity="false">
            <AppenderRef ref="foo.bar" />
        </Logger>
    </Loggers>
</Configuration>

In logs/foo.bar.log appears warn and error messsages. This is what I expected. But in logs/foo.log appears nothing, and I expected info messages there. Also on the console nothing appears, but I expected debug messages. So it does not work as I expected. So what is my mistake?

Also, the argument of LogManager.getLogger must be a name of a logger. But I also see examples where the argument is a class. How does that work, because when I do that only the root logger does log messages.



Solution 1:[1]

There are two kinds of inheritance in Log4j2:

  • inheritance of logger configuration, aka "logger hierarchy" that will cause a logger named "foo.bar.baz" to use the configuration of its nearest ancestor ("foo.bar"). Your "foo.bar" logger uses the configuration you gave it explicitly.
  • inheritance of appenders, aka "additivity" that will cause a logger config to inherit all of its parent's appenders, unless you use additivity="false". In your case the "foo.bar" configuration does not use the appenders of the "foo" configuration.

Another problem with your configuration is that the level of the "foo.bar" logger is set to WARN, so it will never send any logging event with a level different from WARN and ERROR. If you don't want to limit the logger itself, but the appender, you can set level="WARN" on the <AppenderRef>.

If your purpose is to split different log levels into different log files, you can use a mix of level settings (basically a threshold filter) and LevelMatchFilters (or a RoutingAppender):

<Logger name="foo.bar" level="DEBUG" additivity="false">
  <AppenderRef ref="foo.bar" level="WARN" />
  <AppenderRef ref="foo">
    <LevelMatchFilter level="INFO" />
  </AppenderRef>
  <AppenderRef ref="console">
    <LevelMatchFilter level="DEBUG" />
  </AppenderRef>
</Logger>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Piotr P. Karwasz