21

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?

Tony
  • 360
  • 2
  • 11
  • 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 https://stackoverflow.com/questions/60654455/how-to-fix-try-avoiding-to-initialize-the-class-that-caused-initialization-wit?rq=1. – ewramner Jun 27 '22 at 11:00

2 Answers2

23

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>
Thach Van
  • 1,381
  • 16
  • 19
  • 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? – Vitor Ramos Feb 06 '23 at 23:54
  • It's exactly the same to me @VitorRamos. Did you find a solution to this? – Rolf Nyffenegger Mar 13 '23 at 14:54
  • To me it looks like a general problem in the combination of Spring Native and logsstash: https://github.com/spring-projects/spring-boot/issues/33758 – Ben Mar 14 '23 at 08:58
  • @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. – Vitor Ramos Mar 16 '23 at 21:47
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

Vicky
  • 871
  • 9
  • 16