log4j2与Java 11兼容吗?

时间:2018-10-29 15:57:15

标签: java 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。

6 个答案:

答案 0 :(得分:7)

如果有人正在使用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);

答案 1 :(得分:2)

如果收到此消息,则说明您的应用程序未设置为使用多版本jar。 Log4j通过在META-INF / versions / 9中的StackLocator版本中使用Stackwalker来支持Java 9+。根据您的应用程序的工作方式,您可能需要在jar清单中将Multi-Release设置为true。对于Spring Boot jar来说是这样。如果没有多版本支持,您将使用StackLocator的Java 9之前版本,该版本尝试使用Reflection.getCallerClass()。该类已在Java 9中删除。Log4j将使用较慢的方法来计算堆栈位置,但仍然可以使用。因此,警告。

答案 2 :(得分:2)

此消息来自org/apache/logging/log4j/util/StackLocator.<classconstructor>(或log4j2-api.jar等)的log4j-api-2.x.y.jar

之所以收到此消息,是因为某些聪明的人决定将sun.reflect.Reflection.getCallerClass从JRE中删除(猜测他们从Herostratus的书中拉了一页纸之类的东西)。这实际上发生在Openjdk11中。

请记住,如果您使用的log4j2-api.jar版本不是太旧,就不会收到此消息,因为它是multi-release-jar,表示它包含Java9 +的此类的另一种实现( META-INF/versions/9/org/apache/logging/log4j/util/StackLocator.class)不会显示此消息。

但是,如果您使用诸如Spring Boot之类的帮助产品(TM),它具有自己的类加载器,则它可能不是多版本的jar兼容的,因此它将加载与Java8兼容的StackLocator.class而不是与Java9 +兼容,您仍然会收到此消息。

答案 3 :(得分:1)

Log4J2当然是兼容的,它使用JDK Multi-Release featuremore detail

但是...

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>

将所有依赖项添加为“ mvndependency: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 :(得分:0)

  

看起来这部分不适用于Java 11。

在从JDK 8升级到JDK 11之后,我使用LoggerContext以编程方式更新LogLevel设置时遇到了相同的问题。如果LogManager.getContext(boolean)找不到LoggerContext,它将创建并返回一个新实例—更改该实例新对象将无效。在我们的案例中,指定Log4j的LogManager类的类加载器可以解决此问题:

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

答案 5 :(得分:0)

您可以使用类名作为字符串而不是使用Class来设置和更改日志级别。

public static MyLogger loggerFor(String name, String logLevel) {
    Configurator.setRootLevel(Level.toLevel(logLevel, Level.INFO));
    return new MyLogger(LogManager.getLogger(name));
  }