GraalVM(native-image)无法编译logback依赖项

Ton*_*ony 23 java logback graalvm graalvm-native-image

我正在使用当前版本的社区版:GraalVM/native-image 22.1.0

我的项目依赖于日志框架 logback (版本 1.2.3)

如果我想用 graalVM 编译我的“一体式”jar,本机映像会抱怨:

错误:java.util.concurrent.ExecutionException:com.oracle.graal.pointsto.constraints.UnsupportedFeatureException:映像堆中不允许存在 ch.qos.logback.classic.Logger 的实例,因为此类应在映像运行时初始化。要查看该对象是如何实例化的,请使用 --trace-object-instantiation=ch.qos.logback.classic.Logger。

我尝试了许多不同的设置排列,例如--initialize-at-run-time=\<complete list of logback classes\>--initialize-at-run-time

我尝试使用以下命令生成反射配置文件

java -agentlib:native-image-agent=config-output-dir=META-INF/native-image -jar build/myjar-all.jar

并将其添加到配置文件中

-H:ReflectionConfigurationFiles=reflect-config.json

但没有成功。总是收到与 logback 相关的不同错误消息。

所以我的问题是:

有人之前用 graalvm 成功编译过 logback 吗?

Tha*_*Van 26

简短的回答 - 您应该ch.qos.logback添加--initialize-at-build-time

--initialize-at-build-time=ch.qos.logback
Run Code Online (Sandbox Code Playgroud)

有关详细信息,我想发布我为logback使用 GraalVM 所做的工作。

在该build.gradle文件中,我添加了有关在构建时、运行时应包含哪些内容的说明,以及包含反射配置的文件路径:

graalvmNative {
  binaries {
    all {
      resources.autodetect()
    }
    main {
      imageName.set('app') 
      buildArgs.add('--verbose')
      buildArgs.add('--add-opens=java.base/java.nio=ALL-UNNAMED')
      buildArgs.add('--add-opens=java.base/jdk.internal.misc=ALL-UNNAMED')
      buildArgs.add('--add-opens=java.base/jdk.internal.ref=ALL-UNNAMED')
      buildArgs.add('--trace-class-initialization=ch.qos.logback.classic.Logger')
      buildArgs.add('--trace-object-instantiation=ch.qos.logback.core.AsyncAppenderBase$Worker')
      buildArgs.add('--initialize-at-build-time=org.slf4j.LoggerFactory,ch.qos.logback')
      buildArgs.add('--initialize-at-run-time=io.netty')
    }
  }
}

nativeBuild {
  buildArgs('-H:ReflectionConfigurationFiles=../../../src/main/resources/reflection-config.json')
}
Run Code Online (Sandbox Code Playgroud)

这是文件的内容reflection-config.json

[
  {
    "name": "ch.qos.logback.classic.AsyncAppender",
    "allDeclaredConstructors": true,
    "allPublicConstructors": true,
    "allDeclaredMethods": true,
    "allPublicMethods": true,
    "allDeclaredFields": true,
    "allPublicFields": true,
    "allDeclaredClasses": true,
    "allPublicClasses": true
  },
  {
    "name": "ch.qos.logback.classic.encoder.PatternLayoutEncoder",
    "allDeclaredConstructors": true,
    "allPublicConstructors": true,
    "allDeclaredMethods": true,
    "allPublicMethods": true,
    "allDeclaredFields": true,
    "allPublicFields": true,
    "allDeclaredClasses": true,
    "allPublicClasses": true
  },
  {
    "name": "ch.qos.logback.classic.pattern.DateConverter",
    "allDeclaredConstructors": true,
    "allPublicConstructors": true,
    "allDeclaredMethods": true,
    "allPublicMethods": true,
    "allDeclaredFields": true,
    "allPublicFields": true,
    "allDeclaredClasses": true,
    "allPublicClasses": true
  },
  {
    "name": "ch.qos.logback.classic.pattern.LevelConverter",
    "allDeclaredConstructors": true,
    "allPublicConstructors": true,
    "allDeclaredMethods": true,
    "allPublicMethods": true,
    "allDeclaredFields": true,
    "allPublicFields": true,
    "allDeclaredClasses": true,
    "allPublicClasses": true
  },
  {
    "name": "ch.qos.logback.classic.pattern.LineSeparatorConverter",
    "allDeclaredConstructors": true,
    "allPublicConstructors": true,
    "allDeclaredMethods": true,
    "allPublicMethods": true,
    "allDeclaredFields": true,
    "allPublicFields": true,
    "allDeclaredClasses": true,
    "allPublicClasses": true
  },
  {
    "name": "ch.qos.logback.classic.pattern.LoggerConverter",
    "allDeclaredConstructors": true,
    "allPublicConstructors": true,
    "allDeclaredMethods": true,
    "allPublicMethods": true,
    "allDeclaredFields": true,
    "allPublicFields": true,
    "allDeclaredClasses": true,
    "allPublicClasses": true
  },
  {
    "name": "ch.qos.logback.classic.pattern.MessageConverter",
    "allDeclaredConstructors": true,
    "allPublicConstructors": true,
    "allDeclaredMethods": true,
    "allPublicMethods": true,
    "allDeclaredFields": true,
    "allPublicFields": true,
    "allDeclaredClasses": true,
    "allPublicClasses": true
  },
  {
    "name": "ch.qos.logback.classic.pattern.ThreadConverter",
    "allDeclaredConstructors": true,
    "allPublicConstructors": true,
    "allDeclaredMethods": true,
    "allPublicMethods": true,
    "allDeclaredFields": true,
    "allPublicFields": true,
    "allDeclaredClasses": true,
    "allPublicClasses": true
  },
  {
    "name": "ch.qos.logback.core.ConsoleAppender",
    "allDeclaredConstructors": true,
    "allPublicConstructors": true,
    "allDeclaredMethods": true,
    "allPublicMethods": true,
    "allDeclaredFields": true,
    "allPublicFields": true,
    "allDeclaredClasses": true,
    "allPublicClasses": true
  },
  {
    "name": "ch.qos.logback.core.FileAppender",
    "allDeclaredConstructors": true,
    "allPublicConstructors": true,
    "allDeclaredMethods": true,
    "allPublicMethods": true,
    "allDeclaredFields": true,
    "allPublicFields": true,
    "allDeclaredClasses": true,
    "allPublicClasses": true
  }
]
Run Code Online (Sandbox Code Playgroud)

我使用这个logback.xml文件:

<configuration scan="true" scanPeriod="150 seconds">
  <property name="LOG_DIR" value="logs" />

  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender" target="System.out">
    <encoder>
      <charset>UTF-8</charset>
      <pattern>%d{"yyyy-MM-dd'T'HH:mm:ss.SSSXXX", UTC} {%thread} [%-5level] %logger{0} - %msg%n</pattern>
    </encoder>
  </appender>

  <appender name="FILE" class="ch.qos.logback.core.FileAppender">
    <file>${LOG_DIR}/app.log</file>
    <encoder>
      <charset>UTF-8</charset>
      <pattern>%d{"yyyy-MM-dd'T'HH:mm:ss.SSSXXX", UTC} {%thread} [%-5level] %logger - %msg%n</pattern>
    </encoder>
  </appender>

  <appender name="ASYNC_CONSOLE" class="ch.qos.logback.classic.AsyncAppender">
    <discardingThreshold>0</discardingThreshold> <!-- default 20, means drop lower event when has 20% capacity remaining -->
    <appender-ref ref="CONSOLE" />
    <queueSize>1024</queueSize> <!-- default 256 -->
    <includeCallerData>false</includeCallerData> <!-- default false -->
    <neverBlock>false</neverBlock> <!-- default false, set to true to cause the Appender not block the application and just drop the messages -->
  </appender>

  <appender name="ASYNC_FILE" class="ch.qos.logback.classic.AsyncAppender">
    <discardingThreshold>0</discardingThreshold> <!-- default 20, means drop lower event when has 20% capacity remaining -->
    <appender-ref ref="FILE" />
    <queueSize>1024</queueSize> <!-- default 256 -->
    <includeCallerData>false</includeCallerData> <!-- default false -->
    <neverBlock>false</neverBlock> <!-- default false, set to true to cause the Appender not block the application and just drop the messages -->
  </appender>

  <root level="all">
    <appender-ref ref="ASYNC_CONSOLE" />
    <appender-ref ref="ASYNC_FILE" />
  </root>
</configuration>
Run Code Online (Sandbox Code Playgroud)