Совместим ли log4j2 с Java 11?

Я попытался запустить свой проект на последней версии Java 11. Все работает, кроме специального файлового регистратора. Ведение журнала отлично работает на предыдущих версиях Java - 10, 9, 8, но не на Java 11.

Во время работы сервера я вижу только 1 предупреждение:

ВНИМАНИЕ: sun.reflect.Reflection.getCallerClass не поддерживается. Это повлияет на производительность.

Вот моя конфигурация:

<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>

Все регистраторы, кроме userLog работают нормально. Однако userLog пуст.

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)

Обновить:

Добавление level="info" к корневому уровню устраняет проблему.

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

Однако в моем проекте я использовал код, который устанавливал уровень журнала на основе файла свойств. Вот код:

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);
}

Похоже, эта часть больше не работает с Java 11.

Ответ 1

Если кто-то использует Maven и испытывает ту же проблему при сборке плоской банки, вот что я сделал, чтобы исправить эту проблему:

<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>

Важной частью является <Multi-Release>true</Multi-Release>.

Обратите внимание, что код Java, который я сейчас использую для изменения уровня логгеров:

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

Ответ 2

Если вы получаете это сообщение, значит ваше приложение не настроено на использование мульти-релизных фляг. Log4j поддерживает Java 9+, используя Stackwalker в версии StackLocator, которая находится в META-INF/version/9. В зависимости от того, как работает ваше приложение, вам может потребоваться установить для Multi-Release значение true в манифесте jar. Это верно для банок Spring Boot. Без поддержки нескольких выпусков вы будете использовать версию StackLocator до Java 9, которая пытается использовать Reflection.getCallerClass(). Этот класс был удален в Java 9. Log4j обратится к более медленному способу вычисления местоположения стека, но он все еще будет работать. Отсюда и предупреждение.

Ответ 3

Log4J2, конечно, совместим, он использует функцию JDK Multi-Release или в более подробно.

НО...

1) Во-первых, когда вы используете - как и я - интерфейс slf4j, вам нужно использовать другой артефакт Maven, см. 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>

который добавляет все зависимости в виде 'mvn dependency: tree', показывает:

\- 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) И во-вторых, когда вы создаете - как я - один JAR файл, который включает в себя все зависимости, вам также необходимо добавить запись манифеста Multi-Release, см. https://issues.apache.org/jira/browse/LOG4J2-2537 или в моем проекте pom.xml и найдите

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

Ответ 4

Похоже, эта часть больше не работает с Java 11.

Я столкнулся с той же проблемой с программным обновлением настроек LogLevel с использованием LoggerContext после обновления до JDK 11 из JDK 8. Если LogManager.getContext(boolean) не может найти LoggerContext, он создаст и вернет новый экземпляр - изменив этот новый объект не будет иметь никакого эффекта. Указание загрузчика классов для класса Log4j LogManager решило проблему в нашем случае:

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