GraalVM (native-image) can not compile logback dependencies
Asked Answered
B

2

23

I'm using the current version of community edition: GraalVM/native-image 22.1.0

My project has a dependency to the logging framework logback (version 1.2.3)

If I want to compile my "all-in-one" jar with graalVM the native-image complains:

Error: java.util.concurrent.ExecutionException: com.oracle.graal.pointsto.constraints.UnsupportedFeatureException: No instances of ch.qos.logback.classic.Logger are allowed in the image heap as this class should be initialized at image runtime. To see how this object got instantiated use --trace-object-instantiation=ch.qos.logback.classic.Logger.

I played around with many different permutations of settings e.g. --initialize-at-run-time=\<complete list of logback classes\> and --initialize-at-run-time

I tried to generate a reflection configuration file with

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

and added it to the config file with

-H:ReflectionConfigurationFiles=reflect-config.json

But without success. Always getting different error messages all related to logback.

So my question:

Has anyone got logback successfully compiled with graalvm before?

Bhopal answered 27/6, 2022 at 10:19 Comment(1)
Have you tried changing the loggers in your code from static to normal fields? It is common to use static loggers with logback, but not required. If you use "private final Logger" instead of "private static final Logger" the initialization will happen at runtime. EDIT: see also #60654955.Christianize
P
27

Short answer - you should add ch.qos.logback to --initialize-at-build-time:

--initialize-at-build-time=ch.qos.logback

For details, I would like to post what I have done to make logback working with GraalVM.

In the build.gradle file, I added instructions about what should be included at build time, at run time and also path to the file contains reflection configuration:

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

Here is the content of reflection-config.json file:

[
  {
    "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
  }
]

I use this logback.xml file:

<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>
Preselector answered 23/9, 2022 at 6:19 Comment(5)
I have exacly the same problem, but when a add graalvmNative it doesn't change anything, it looks like the block is completely ignored, even when I include --verbose or --trace-object-instantiation=ch.qos.logback.classic.Logger, any ideas?Recliner
It's exactly the same to me @VitorRamos. Did you find a solution to this?Maestoso
To me it looks like a general problem in the combination of Spring Native and logsstash: github.com/spring-projects/spring-boot/issues/33758Harmonicon
@RolfNyffenegger since I noticed that the whole block was ignored, I thought it could be a problem with the gradle plugin, so I tried using maven with the build arg and it worked.Recliner
In some cases, you may also want to add -H:IncludeResources=logback.xmlWomanly
C
0

Maybe I am late to answer but this may help someone. I was also facing issues with logback.xml initialisation.

While searching for solution I found that spring suggests to name the file as logback-spring.xml instead of logback.xml

See here https://docs.spring.io/spring-boot/docs/2.1.13.RELEASE/reference/html/boot-features-logging.html

enter image description here

Canaveral answered 30/8, 2023 at 12:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.