Is log4j2 compatible with Java 11?
Asked Answered
S

6

76

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.

Slighting answered 29/10, 2018 at 15:57 Comment(5)
anything that you've observed changing from Java10 to Java11? Looks strange.Milewski
I didn't change anything, same jar, same env., different JVMs.Slighting
@nullpointer I can see the same warning when running my app on JDK 11 (with 2.11.1 and 2.11.0). There is no warning on JDK 10. My project is large so it's hard to create a reproducible example.Forecastle
"Seems like this part is no longer work with Java 11." - The warning doesn't say it is not working. It says it is working ... slowly.Addie
Yes, but those code is no longer works with Java 11.Slighting
C
90

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);
Clasp answered 15/2, 2019 at 16:56 Comment(6)
This fixed mine. I have <Root level="DEBUG"> in my log4j2.xml.Zebu
Works for me as well. Using Gradle, I've added "Multi-Release": true to jar { manifest.attributes.Umber
This fixed my logging problem but created a host of other problems with Jackson in AWS version 1 jars. After several hours of trying to fix it, I had to roll it back.Elum
for gradle i did this jar { manifest { attributes( "Manifest-Version": "1.0", "Multi-Release": true ) } from { configurations.compileClasspath.collect { it.isDirectory() ? it : zipTree(it) } } }Tapir
I placed the <manifestEntries> <Multi-Release>true</Multi-Release> </manifestEntries> in the assembly plugin and it worked.Illness
but can you exaplain the reason(why this syntax can fix, and does the warning performance issue goes away?)Abeokuta
C
26

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

<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j18-impl</artifactId>
<version>2.12.1</version>
</dependency>

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>
Callisthenics answered 22/8, 2019 at 20:52 Comment(3)
this is the right answer for java 11. log4j never stops to surprise me.Tiffie
Thanks a lot! Your code added that into "<artifactId>maven-assembly-plugin</artifactId>", and your code resolved my problem. One other answer which added it to "<artifactId>maven-shade-plugin</artifactId>" does not resolve my problem.Weywadt
I found it was only necessary to add <archive><manifestEntries><Multi-Release>true</Multi-Release> to the pom.xml, after descriptorRefsRouble
H
20

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.

Heckle answered 30/1, 2019 at 23:36 Comment(7)
Thanks. However, the multi-release jar is not a must. I don't need it so I don't pack it as multi-release. Why log4j2 just can't check is the required class is available and proceed if it is? What the point to rely on the manifest? That could be wrong configured or misleading. Seem like log4j2 bug to me.Slighting
You are right that it is not a must. If you don't care that the performance of your application will be impacted and can live with the warning then there is no problem. The point of relying on the manifest is that is the standard way libraries are supposed to support multiple Java versions from Java 9 on. If you don't like the way it works I am afraid you will have to take that up with the OpenJDK folks. Using other methods to determine the correct implementation to use would have caused other problems as having classes built for multiple java versions on the classpath can cause tools to fail.Heckle
Sorry. I still do not understand the problem. There is System.getProperty("java.version") that could be used by log4j2 in runtime. Am I wrong?Slighting
Yes, you are wrong. The StackWalker API can only be compiled with a Java 9+ compiler and will have a class version number for Java 9. The rest of Log4j is compiled for Java 7. If a Java 9 class is located alongside classes targeted for Java 7 many tools will fail when they encounter the class. They won't fail when they are packaged in the multi-release location since that was invalid prior to Java 9. So the problem has nothing to do with just being able to detect a version.Heckle
Aha, so log4j2 is packed as multijar... That's bad news. Maybe it was better and simpler just to porvide different builds for java7+ and for java9+. Just thoughts.Slighting
What does that even mean?Catalectic
@Catalectic What does what mean? Multi-release jar? See baeldung.com/java-multi-release-jar for a decent explanation. The formal documentation is at openjdk.java.net/jeps/238.Heckle
F
7

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);
Fearful answered 1/5, 2019 at 18:27 Comment(0)
T
7

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.

Trooper answered 9/3, 2020 at 5:43 Comment(0)
I
2

Similar problem with Java 17. I've tried several solutions above (editing pom.xml to include manifestEntries tags, log4j.xml, etc) with no success in creating a runnable JAR from within Eclipse without the error. The POM for whatever reason would not create the multi-release tag inside the ANT build script.

The workarounds were to either select "Copy required libraries into a sub-folder next to the generated JAR" or,

"Extract required libraries into generated JAR" and choose the "Save as ANT script" option into the project. Edit the script by adding the Multi-Release attribute to the manifest tag:

<manifest>
   ...
   <attribute name="Multi-Release" value="true"/>
</manifest>

Within Eclipse, right-click the updated script and select Run-As ANT-build. The new runnable JAR worked after that.

Ingenuous answered 28/4, 2023 at 17:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.