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>