GraalVM 本机图像构建无法找到 log4j appender class

GraalVM native-image build fails to find log4j appender class

我正在尝试将我的 .jar 项目转换为本机图像,因为我需要在不支持 Java 的设备中 运行 它。 为此,我安装了 GraalVM 和所有必需的依赖项,并且本机映像构建工作完美(或者至少,似乎,因为它在过程中没有给出任何错误)。

我用于构建的命令是:

/usr/lib/jvm/graalvm/bin/native-image -jar MyApp.jar MyApp --enable-http --enable-https --no-fallback -H:+ReportExceptionStackTraces

问题是,当我尝试 运行 本机文件时,出现异常提示无法找到 log4j class,因此在执行期间我没有应用程序日志:

log4j:ERROR Could not instantiate class [org.apache.log4j.RollingFileAppender].
java.lang.ClassNotFoundException: org.apache.log4j.RollingFileAppender
        at java.lang.Class.forName(DynamicHub.java:1338)
        at java.lang.Class.forName(DynamicHub.java:1313)
        at org.apache.log4j.helpers.Loader.loadClass(Loader.java:198)
        at org.apache.log4j.helpers.OptionConverter.instantiateByClassName(OptionConverter.java:327)
        at org.apache.log4j.helpers.OptionConverter.instantiateByKey(OptionConverter.java:124)
        at org.apache.log4j.PropertyConfigurator.parseAppender(PropertyConfigurator.java:785)
        at org.apache.log4j.PropertyConfigurator.parseCategory(PropertyConfigurator.java:768)
        at org.apache.log4j.PropertyConfigurator.configureRootCategory(PropertyConfigurator.java:648)
        at org.apache.log4j.PropertyConfigurator.doConfigure(PropertyConfigurator.java:514)
        at org.apache.log4j.PropertyConfigurator.doConfigure(PropertyConfigurator.java:580)
        at org.apache.log4j.helpers.OptionConverter.selectAndConfigure(OptionConverter.java:526)
        at org.apache.log4j.LogManager.<clinit>(LogManager.java:127)
        at org.slf4j.impl.Log4jLoggerFactory.<init>(Log4jLoggerFactory.java:66)
        at org.slf4j.impl.StaticLoggerBinder.<init>(StaticLoggerBinder.java:72)
        at org.slf4j.impl.StaticLoggerBinder.<clinit>(StaticLoggerBinder.java:45)
        at org.slf4j.LoggerFactory.bind(LoggerFactory.java:150)
        at org.slf4j.LoggerFactory.performInitialization(LoggerFactory.java:124)
        at org.slf4j.LoggerFactory.getILoggerFactory(LoggerFactory.java:412)
        at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:357)
        at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:383)
        at com.test.MyApp.<clinit>(MyApp.java:40)
log4j:ERROR Could not instantiate appender named "file".
log4j:WARN No appenders could be found for logger (com.test.MyApp).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.

当我 运行 .jar 到 java -jar 时,它工作得很好。提取 jar,我可以看到其中的所有 log4j 文件,包括给出错误的 RollingFileAppender class(它是一个带阴影的 jar)。问题不仅在于 RollingFileAppender,如果我尝试使用另一个不同的附加程序,我仍然会在另一个 class 上遇到相同的错误。所以我就是想不通构建有什么问题。

这是我的 pom.xml 文件中的所有依赖项:

    <dependencies>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.3.2</version>
        </dependency>       
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.25</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.25</version>
        </dependency>
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-jaxrs</artifactId>
            <version>3.0.19.Final</version>
        </dependency>
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-jackson2-provider</artifactId>
            <version>3.0.19.Final</version>
        </dependency>
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-client</artifactId>
            <version>3.0.19.Final</version>
        </dependency>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-api</artifactId>
            <version>7.0</version>
            <scope>provided</scope>
        </dependency>

        (These last 2 were not in the original project, I tried adding them to see if it'd help but nothing changed, still get the same error)
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>apache-log4j-extras</artifactId>
            <version>1.2.17</version>
        </dependency>
    </dependencies>

还有我的 maven-shade-plugin 配置:

          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>3.2.4</version>
            <executions>
              <execution>
                <phase>package</phase>
                <goals>
                  <goal>shade</goal>
                </goals>
              </execution>
            </executions>
          </plugin>

任何关于此事的线索都将不胜感激,我已经尝试解决这个问题好几天了,但没有成功。

提前致谢!

有趣的是,在发布这个问题后不久,我就找到了答案。它与 GraalVM 的反射配置有关。修复实际上非常简单:

首先你 运行 你的 jar 使用一个特殊的 GraalVM 选项:

<GRAALVM_HOME>/bin/java -agentlib:native-image-agent=config-output-dir=<DIRECTORY_YOU_WANT_THE_FILES_TO_BE_GERERATED_AT> -jar <JAR_FILE>.jar

这将生成一堆 .json 文件,这些文件将有助于配置 GraalVM native-image 构建:

jni-config.json
predefined-classes-config.json
proxy-config.json
reflect-config.json
resource-config.json
serialization-config.json

获得这些文件后,您可以执行将它们作为参数传递的构建:

<GRAALVM_HOME>/bin/native-image -jar <JAR_FILE>.jar <NATIVE_IMAGE_NAME> -H:ConfigurationFileDirectories=<DIRECTORY_WHERE_YOUR_JSON_FILES_ARE_AT>

就是这样!构建完成后,本机映像 运行 完全没有任何问题,所有日志都按预期工作。

如果需要,您还可以将 .json 文件添加到可从类路径访问的 META-INF/native-image 目录中,例如在您的 src/main/resources 目录中。 这样,您在调用 native-image 构建时不需要将目录作为参数传递。

我所有这些有用信息的来源是: https://simply-how.com/fix-graalvm-native-image-compilation-issues

我希望这对遇到同样问题的其他人有所帮助:)