Is log4j2 compatible with Java 11?

JavaLog4j2Java 11

Java Problem Overview


I tried to run my project on the latest Java 11. Everything works, except the specific file logger. Logging works fine on previous Java versions - 10, 9, 8, but not on Java 11.

During server run I see only 1 warning:

> WARNING: sun.reflect.Reflection.getCallerClass is not supported. This > will impact performance.

Here is my configuration:

<Configuration>

    <Appenders>

        <RollingFile name="postgresDBLog" fileName="${sys:logs.folder}/postgres.log"
              filePattern="${sys:logs.folder}/archive/postgres.log.%d{yyyy-MM-dd}">
            <PatternLayout>
                <pattern>%d{HH:mm:ss.SSS} - %msg%n</pattern>
            </PatternLayout>
            <Policies>
                <TimeBasedTriggeringPolicy/>
            </Policies>
        </RollingFile>

        <RollingFile name="workersLog" fileName="${sys:logs.folder}/worker.log"
                     filePattern="${sys:logs.folder}/archive/worker.log.%d{yyyy-MM-dd}">
            <PatternLayout>
                <pattern>%d{HH:mm:ss.SSS} - %msg%n</pattern>
            </PatternLayout>
            <Policies>
                <TimeBasedTriggeringPolicy/>
            </Policies>
        </RollingFile>

        <RollingFile name="statsLog" fileName="${sys:logs.folder}/stats.log"
                     filePattern="${sys:logs.folder}/archive/stats.log.%d{yyyy-MM-dd}">
            <PatternLayout>
                <pattern>%msg%n</pattern>
            </PatternLayout>
            <Policies>
                <TimeBasedTriggeringPolicy/>
            </Policies>
        </RollingFile>

        <RollingFile name="userLog" fileName="${sys:logs.folder}/blynk.log"
                     filePattern="${sys:logs.folder}/archive/blynk.log.%d{yyyy-MM-dd}">
            <PatternLayout>
                <pattern>%d{HH:mm:ss.SSS} %-5level- %msg%n</pattern>
            </PatternLayout>
            <Policies>
                <TimeBasedTriggeringPolicy/>
            </Policies>
        </RollingFile>

    </Appenders>

    <Loggers>

        <Logger name="cc.blynk.server.workers" level="debug" additivity="false">
            <appender-ref ref="workersLog"/>
        </Logger>
        <Logger name="cc.blynk.server.workers.StatsWorker" level="debug" additivity="false">
            <appender-ref ref="statsLog"/>
        </Logger>
        <Logger name="cc.blynk.server.db" level="debug" additivity="false">
            <appender-ref ref="postgresDBLog"/>
        </Logger>
        <Logger name="com.zaxxer.hikari" level="OFF" additivity="false">
        </Logger>

        <Logger name="org.asynchttpclient.netty.channel" level="OFF" additivity="false" />

        <!-- turn off netty errors in debug mode for native library loading
         https://github.com/blynkkk/blynk-server/issues/751 -->
        <Logger name="io.netty" level="INFO" additivity="false" />

        <Root>
            <AppenderRef ref="userLog"/>
        </Root>

    </Loggers>
</Configuration>

All loggers, except userLog works fine. However, userLog is empty.

log4j2 version 2.11.1

Ubuntu 16.04.5 LTS

java version "11.0.1" 2018-10-16 LTS
Java(TM) SE Runtime Environment 18.9 (build 11.0.1+13-LTS)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.1+13-LTS, mixed mode)

Update:

Adding level="info" to the root level fixes the issue.

    <Root level="info">
        <AppenderRef ref="userLog"/>
    </Root>

However, in my project I was using a code that was setting a log level based on properties file. Here is a code:

private static void changeLogLevel(String level) {
    Level newLevel = Level.valueOf(level);
    LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
    Configuration conf = ctx.getConfiguration();
    conf.getLoggerConfig(LogManager.ROOT_LOGGER_NAME).setLevel(newLevel);
    ctx.updateLoggers(conf);
}

Seems like this part is no longer work with Java 11.

Java Solutions


Solution 1 - Java

If someone is using Maven and is having the same issue while assembling a flat jar, here is what I did to fix the same issue:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>3.2.1</version>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
            <configuration>
                <transformers>
                    <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                        <mainClass>foo.bar.Generate</mainClass>
                        <manifestEntries>
                            <Multi-Release>true</Multi-Release>
                        </manifestEntries>
                    </transformer>
                    <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
                </transformers>
            </configuration>
        </execution>
    </executions>
</plugin>

The important part is <Multi-Release>true</Multi-Release>.

Note that the Java code I'm using now to change loggers level is:

Configurator.setAllLevels("foo.bar", Level.DEBUG);

Solution 2 - Java

Log4J2 is of course compatible it uses the JDK Multi-Release feature or in more detail.

BUT...

  1. First, when you are using - like me - the slf4j interface, you need to use a different Maven artefact, see http://logging.apache.org/log4j/2.x/log4j-slf4j-impl/index.html

    org.apache.logging.log4j log4j-slf4j18-impl 2.12.1

which adds all dependencies as 'mvn dependency:tree' reveals:

\- org.apache.logging.log4j:log4j-slf4j18-impl:jar:2.12.1:compile
[INFO] +- org.slf4j:slf4j-api:jar:1.8.0-alpha2:compile
[INFO] +- org.apache.logging.log4j:log4j-api:jar:2.12.1:compile
[INFO] \- org.apache.logging.log4j:log4j-core:jar:2.12.1:runtime

2) And second, when you are creating - like me - one single JAR, which includes all dependencies, you need to add the Multi-Release manifest entry as well, see https://issues.apache.org/jira/browse/LOG4J2-2537 or in my project's pom.xml and search for

<Multi-Release>true</Multi-Release>

Solution 3 - Java

If you are getting this message then your application is not setup to use multi-release jars. Log4j supports Java 9+ by using Stackwalker in a version of StackLocator that is located in META-INF/versions/9. Depending on how your application works, you may need to have Multi-Release set to true in the jar manifest. This is true for Spring Boot jars. Without multi-release support you will use the pre-Java 9 version of StackLocator which tries to use Reflection.getCallerClass(). That class was removed in Java 9. Log4j will fall back to a slower way to calculate stack locations but it will still work. Hence the warning.

Solution 4 - Java

> Seems like this part is no longer work with Java 11.

I ran into the same problem with programmatically updating LogLevel settings using the LoggerContext after upgrading to JDK 11 from JDK 8. If the LogManager.getContext(boolean) can't find the LoggerContext it will create and return a new instance — changing that new object will have no effect. Specifying the classloader of Log4j's LogManager class fixed the issue in our case:

LoggerContext ctx = (LoggerContext) LogManager.getContext(LogManager.class.getClassLoader(), false);

Solution 5 - Java

This message comes from org/apache/logging/log4j/util/StackLocator.<classconstructor> which is part of log4j2-api.jar (or log4j-api-2.x.y.jar etc).

You get this message because some smart guys decided sun.reflect.Reflection.getCallerClass has to be removed from JRE (guess they pulled a page from Herostratus's book or something). This actually happened in Openjdk11.

Mind you, you shouldn't get this message if you have a not-too-old version of log4j2-api.jar, as it is a multi-release-jar meaning it contains another implementation of this class for Java9+ (META-INF/versions/9/org/apache/logging/log4j/util/StackLocator.class) which doesn't give this message.

But, if you use some Helping Product(TM), such as Spring Boot, that has its own classloader, it might not be multi-relase-jar compatible, so it loads the Java8 compatible StackLocator.class instead of the Java9+ compatible, and you still get this message.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDmitriy DumanskiyView Question on Stackoverflow
Solution 1 - JavadadoonetView Answer on Stackoverflow
Solution 2 - JavaSvante SchubertView Answer on Stackoverflow
Solution 3 - JavargoersView Answer on Stackoverflow
Solution 4 - JavaDamienView Answer on Stackoverflow
Solution 5 - JavaLorinczy ZsigmondView Answer on Stackoverflow