Log4J2 配置失败:Log4J2 RabbitMQ Appender with Spring-AMQP/Spring-Rabbit

Log4J2 Configuration fails: Log4J2 RabbitMQ Appender with Spring-AMQP/Spring-Rabbit

我正在尝试将 Log4J2 Appender 集成到 RabbitMQ 中一个已经在工作和记录的 java 应用程序。

应用程序构建为 gradle 项目。在集成 spring-rabbit 之前,build.gradle 文件如下所示:

group 'Name'
version '1.18.7'

apply plugin: 'java'
apply plugin: 'com.github.johnrengelman.shadow'

sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
    [...]
    compile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.10.0'
    compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.10.0'
    compile group: 'com.lmax', name: 'disruptor', version: '3.3.7'
    [...]
}

jar {
    manifest {
        attributes 'Main-Class': 'the.main.Clazz'
    }
}


buildscript {
    repositories {
        maven {
            url "https://plugins.gradle.org/m2/"
        }
    }
    dependencies {
        classpath group: 'com.github.jengelman.gradle.plugins', name: 'shadow', version: '2.0.2'
    }
}

位于 src/resources 文件夹中的 log4j2.xml 文件包含以下内容(在 rabbitMQ 之前):

<?xml version="1.0" encoding="UTF-8"?>

<Configuration monitorInterval="15">
    <Appenders>
        <Console name="STDOUT">
            <PatternLayout>
                <Pattern>%d [%highlight{%-6p}{STYLE=DEFAULT, noConsoleNoAnsi=true}] %C{1}.%M(%F:%L) - %m%n%throwable</Pattern>
            </PatternLayout>
        </Console>
        <RandomAccessFile name="ASYNC_FILE" fileName="logs/app.log" immediateFlush="false" append="true">
            <PatternLayout>
                <Pattern>%d [%-6p] %C{1}.%M(%F:%L) - %m%n%throwable</Pattern>
            </PatternLayout>
        </RandomAccessFile>
    </Appenders>
    <Loggers>
        <Root level="info">
            <AppenderRef ref="STDOUT"/>
        </Root>
        <Logger name="my.package" level="info" additivity="false">
            <AppenderRef ref="ASYNC_FILE"/>
        </Logger>
    </Loggers>
</Configuration>

应用程序 运行 很好,来自 IDE 和 JAR,使用 shadowJar 插件构建。

现在,谜团开始了。只需添加 spring-rabbit 依赖项...

compile group: 'org.springframework.amqp', name: 'spring-rabbit', version: '2.0.2.RELEASE'

...日志记录开始表现得很奇怪。从 IDE 开始,一切正常。将以下附加程序添加到 log4j2.xml 工作正常:

    <RabbitMQ name="RABBIT_MQ"
              host="my.host.name" port="5672" user="logger" password="logger" virtualHost="loggerhost"
              exchange="logs" exchangeType="fanout" declareExchange="false"
              applicationId="app-xyz" routingKeyPattern="%X{applicationId}.%c.%p"
              contentType="text/plain" contentEncoding="UTF-8" generateId="true" deliveryMode="NON_PERSISTENT"
              charset="UTF-8"
              senderPoolSize="3" maxSenderRetries="5">
        <PatternLayout>
            <Pattern>%d [%-6p] %C{1}.%M(%F:%L) - %m%n%throwable</Pattern>
        </PatternLayout>
    </RabbitMQ>

我看到通过 RabbitMQ 服务器传送的日志消息。

但是当我构建并 运行 使用 :shadowJar 构建的 JAR 文件时,日志记录停止工作。在 STDOUT 上,我看到以下内容:

ERROR StatusLogger Unrecognized format specifier [d]
ERROR StatusLogger Unrecognized conversion specifier [d] starting at position 16 in conversion pattern.
ERROR StatusLogger Unrecognized format specifier [thread]
ERROR StatusLogger Unrecognized conversion specifier [thread] starting at position 25 in conversion pattern.
ERROR StatusLogger Unrecognized format specifier [level]
ERROR StatusLogger Unrecognized conversion specifier [level] starting at position 35 in conversion pattern.
ERROR StatusLogger Unrecognized format specifier [logger]
ERROR StatusLogger Unrecognized conversion specifier [logger] starting at position 47 in conversion pattern.
ERROR StatusLogger Unrecognized format specifier [msg]
ERROR StatusLogger Unrecognized conversion specifier [msg] starting at position 54 in conversion pattern.
ERROR StatusLogger Unrecognized format specifier [n]
ERROR StatusLogger Unrecognized conversion specifier [n] starting at position 56 in conversion pattern.
ERROR StatusLogger No log4j2 configuration file found. Using default configuration: logging only errors to the console. Set system property 'log4j2.debug' to show Log4j2 internal initialization logging.
ERROR StatusLogger Unrecognized format specifier [d]
ERROR StatusLogger Unrecognized conversion specifier [d] starting at position 16 in conversion pattern.
ERROR StatusLogger Unrecognized format specifier [thread]
ERROR StatusLogger Unrecognized conversion specifier [thread] starting at position 25 in conversion pattern.
ERROR StatusLogger Unrecognized format specifier [level]
ERROR StatusLogger Unrecognized conversion specifier [level] starting at position 35 in conversion pattern.
ERROR StatusLogger Unrecognized format specifier [logger]
ERROR StatusLogger Unrecognized conversion specifier [logger] starting at position 47 in conversion pattern.
ERROR StatusLogger Unrecognized format specifier [msg]
ERROR StatusLogger Unrecognized conversion specifier [msg] starting at position 54 in conversion pattern.
ERROR StatusLogger Unrecognized format specifier [n]
ERROR StatusLogger Unrecognized conversion specifier [n] starting at position 56 in conversion pattern.

当我使用 -Dlog4j2.debug 选项启动应用程序 JAR 时,我看到很多消息,有些似乎说无法加载配置(仍然在同一个地方)。摘录如下:

DEBUG StatusLogger Using configurationFactory org.apache.logging.log4j.core.config.ConfigurationFactory$Factory@2a33fae0
ERROR StatusLogger No log4j2 configuration file found. Using default configuration: logging only errors to the console. Set system property 'log4j2.debug' to show Log4j2 internal initialization logging.

我什至使用了-Dlog4j.configurationFile选项,直接指向文件系统中的XML,结果还是一样。

同样,从 IDE 而不是通过 java -jar ... 启动应用程序工作正常。在我看来,好像 spring-rabbitmq 依赖项带来了一些额外的 log4j 东西,这些东西干扰了我的配置。我完全是在浑水摸鱼

因此,经过大量研究后,我可以用以下内容回答我的问题。

不是 只需添加 Spring-Rabbit-MQ org.springframework.amqp:spring-rabbit 依赖项就足够了Log4J2 配置失败。这是与另一个依赖项的组合,我的示例中未列出该依赖项:com.fasterxml.jackson.core:jackson-databind.

总结一下。这不起作用:

compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.3'
compile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.10.0'
compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.10.0'
compile group: 'com.lmax', name: 'disruptor', version: '3.3.7'
compile group: 'org.springframework.amqp', name: 'spring-rabbit', version: '2.0.2.RELEASE'

但这有效(没有 Spring):

compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.3'
compile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.10.0'
compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.10.0'
compile group: 'com.lmax', name: 'disruptor', version: '3.3.7'

这有效(没有杰克逊):

compile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.10.0'
compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.10.0'
compile group: 'com.lmax', name: 'disruptor', version: '3.3.7'
compile group: 'org.springframework.amqp', name: 'spring-rabbit', version: '2.0.2.RELEASE'

因此 Jackson 和 Spring 的组合破坏了 Log4J2。有趣的是,没有 Jackson,即使 Jackson 被大量使用,我的程序也能正常工作。让我们来看看,gradle dependecies 是怎么说的:

compile - Dependencies for source set 'main' (deprecated, use 'implementation ' instead).
+--- org.apache.logging.log4j:log4j-api:2.10.0
+--- org.apache.logging.log4j:log4j-core:2.10.0
|    \--- org.apache.logging.log4j:log4j-api:2.10.0
+--- com.lmax:disruptor:3.3.7
\--- org.springframework.amqp:spring-rabbit:2.0.2.RELEASE
     +--- org.springframework.amqp:spring-amqp:2.0.2.RELEASE
     |    \--- org.springframework:spring-core:5.0.3.RELEASE
     |         \--- org.springframework:spring-jcl:5.0.3.RELEASE
     +--- com.rabbitmq:amqp-client:5.1.2
     |    \--- org.slf4j:slf4j-api:1.7.25 -> 1.8.0-alpha2
     +--- com.rabbitmq:http-client:1.3.1.RELEASE
     |    +--- org.apache.httpcomponents:httpclient:4.5.3
     |    |    +--- org.apache.httpcomponents:httpcore:4.4.6
     |    |    +--- commons-logging:commons-logging:1.2
     |    |    \--- commons-codec:commons-codec:1.9 -> 1.11
     |    \--- com.fasterxml.jackson.core:jackson-databind:2.9.2
     |         +--- com.fasterxml.jackson.core:jackson-annotations:2.9.0
     |         \--- com.fasterxml.jackson.core:jackson-core:2.9.2
     +--- org.springframework:spring-context:5.0.3.RELEASE
     |    +--- org.springframework:spring-aop:5.0.3.RELEASE
     |    |    +--- org.springframework:spring-beans:5.0.3.RELEASE
     |    |    |    \--- org.springframework:spring-core:5.0.3.RELEASE (*)
     |    |    \--- org.springframework:spring-core:5.0.3.RELEASE (*)
     |    +--- org.springframework:spring-beans:5.0.3.RELEASE (*)
     |    +--- org.springframework:spring-core:5.0.3.RELEASE (*)
     |    \--- org.springframework:spring-expression:5.0.3.RELEASE
     |         \--- org.springframework:spring-core:5.0.3.RELEASE (*)
     +--- org.springframework:spring-messaging:5.0.3.RELEASE
     |    +--- org.springframework:spring-beans:5.0.3.RELEASE (*)
     |    \--- org.springframework:spring-core:5.0.3.RELEASE (*)
     +--- org.springframework:spring-tx:5.0.3.RELEASE
     |    +--- org.springframework:spring-beans:5.0.3.RELEASE (*)
     |    \--- org.springframework:spring-core:5.0.3.RELEASE (*)
     +--- org.springframework:spring-web:5.0.3.RELEASE
     |    +--- org.springframework:spring-beans:5.0.3.RELEASE (*)
     |    \--- org.springframework:spring-core:5.0.3.RELEASE (*)
     \--- org.springframework.retry:spring-retry:1.2.1.RELEASE
          \--- org.springframework:spring-core:4.3.9.RELEASE -> 5.0.3.RELEASE (*)

哦,等等,Spring AMQP 中还有一个 Jackson。好吧,这解决了谜题,为什么在删除依赖项后我仍然可以使用 Jackson。但这对我来说非常不透明,并且依赖于 Spring AMQP 包来为我提供核心依赖项。所以我最后做的是这个,它似乎有效:

compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.3'
compile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.10.0'
compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.10.0'
compile group: 'com.lmax', name: 'disruptor', version: '3.3.7' version: '2.12.0'
compile (group: 'org.springframework.amqp', name: 'spring-rabbit', version: '2.0.2.RELEASE') {
    exclude group: 'com.fasterxml.jackson.core', module: 'jackson-databind'
}

依赖关系树现在如下所示:

compile - Dependencies for source set 'main' (deprecated, use 'implementation ' instead).
+--- com.fasterxml.jackson.core:jackson-databind:2.9.3
|    +--- com.fasterxml.jackson.core:jackson-annotations:2.9.0
|    \--- com.fasterxml.jackson.core:jackson-core:2.9.3
+--- org.apache.logging.log4j:log4j-api:2.10.0
+--- org.apache.logging.log4j:log4j-core:2.10.0
|    \--- org.apache.logging.log4j:log4j-api:2.10.0
+--- com.lmax:disruptor:3.3.7
\--- org.springframework.amqp:spring-rabbit:2.0.2.RELEASE
     +--- org.springframework.amqp:spring-amqp:2.0.2.RELEASE
     |    \--- org.springframework:spring-core:5.0.3.RELEASE
     |         \--- org.springframework:spring-jcl:5.0.3.RELEASE
     +--- com.rabbitmq:amqp-client:5.1.2
     |    \--- org.slf4j:slf4j-api:1.7.25 -> 1.8.0-alpha2
     +--- com.rabbitmq:http-client:1.3.1.RELEASE
     |    \--- org.apache.httpcomponents:httpclient:4.5.3
     |         +--- org.apache.httpcomponents:httpcore:4.4.6
     |         +--- commons-logging:commons-logging:1.2
     |         \--- commons-codec:commons-codec:1.9 -> 1.11
     +--- org.springframework:spring-context:5.0.3.RELEASE
     |    +--- org.springframework:spring-aop:5.0.3.RELEASE
     |    |    +--- org.springframework:spring-beans:5.0.3.RELEASE
     |    |    |    \--- org.springframework:spring-core:5.0.3.RELEASE (*)
     |    |    \--- org.springframework:spring-core:5.0.3.RELEASE (*)
     |    +--- org.springframework:spring-beans:5.0.3.RELEASE (*)
     |    +--- org.springframework:spring-core:5.0.3.RELEASE (*)
     |    \--- org.springframework:spring-expression:5.0.3.RELEASE
     |         \--- org.springframework:spring-core:5.0.3.RELEASE (*)
     +--- org.springframework:spring-messaging:5.0.3.RELEASE
     |    +--- org.springframework:spring-beans:5.0.3.RELEASE (*)
     |    \--- org.springframework:spring-core:5.0.3.RELEASE (*)
     +--- org.springframework:spring-tx:5.0.3.RELEASE
     |    +--- org.springframework:spring-beans:5.0.3.RELEASE (*)
     |    \--- org.springframework:spring-core:5.0.3.RELEASE (*)
     +--- org.springframework:spring-web:5.0.3.RELEASE
     |    +--- org.springframework:spring-beans:5.0.3.RELEASE (*)
     |    \--- org.springframework:spring-core:5.0.3.RELEASE (*)
     \--- org.springframework.retry:spring-retry:1.2.1.RELEASE
          \--- org.springframework:spring-core:4.3.9.RELEASE -> 5.0.3.RELEASE (*)

我必须承认,我不完全理解为什么两个 Jackson 依赖项的组合会降低 Log4J2 配置,但现在它适用于此策略:

  • 显式声明 Jackson 依赖项(使应用程序使用 Jackson 变得透明)
  • 将杰克逊从 Spring 依赖中排除

我真正不明白的是:为什么所有组合都在 IDE 中工作,但后来在 运行 JAR 时失败?

这是已知的issue with log4j, reported over 3 years ago. It comes up when you use the shadowjar plugin. See shadowjar issue here

您可以阅读这两张票以获得更好的理解,但如果您正在寻找点击式解决方案,只需使用 this gradle 插件:

plugins {
  id "com.github.johnrengelman.shadow" version "2.0.2"
  id "de.sebastianboegl.shadow.transformer.log4j" version "2.2.0"
}

...或恢复到 log4j 2.0.2.