Search code examples
spring-bootlogbackgraalvm-native-imagejanino

Spring Boot native image does not print logs when logback is configured with conditional


Summary

Edit: The issue is only relevant from Spring Boot 3.0.0 to 3.0.2. It has been addressed in 3.0.3. See last section for details.

I have a very simple Spring Boot 3.0.2 app with only janino as added dependency. When I use an if-else conditional in the logback configuration, then no logs are printed by logback when the application is run as a native image. However, printing with System.out works fine.

For compilation all reachability metadata has been provided that was given by the tracing agent when running the jar in the JVM. The application exits with a return code of 0.

Without an if-else condition, everything works as expected.

My questions are:

  • Does anybody know how to fix this?
  • What's the best approach to further troubleshoot this?

The code

The logback-spring.xml with a conditional just to have a conditional:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <if condition='property("WITH_DEBUG").equalsIgnoreCase("true")'>
    <then>
      <include resource="org/springframework/boot/logging/logback/base.xml"/>
    </then>
    <else>
      <include resource="org/springframework/boot/logging/logback/base.xml"/>
    </else>
  </if>
</configuration>

The main (and only) class:

@SpringBootApplication
public class LogbackIfElseApplication {

  private static final Logger LOG = LoggerFactory.getLogger(LogbackIfElseApplication.class);

  public static void main(String[] args) {
    SpringApplication.run(LogbackIfElseApplication.class, args);
  }

  @Bean
  ApplicationRunner applicationRunner() {
    return args -> {
      LOG.info("Hello Logback!");
      System.out.println("Hello stdout");
    };
  }

}

My build script:

plugins {
    java
    id("org.springframework.boot") version "3.0.2"
    id("io.spring.dependency-management") version "1.1.0"
    id("org.graalvm.buildtools.native") version "0.9.18"
}

group = "foo"
version = "0.0.1-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_17

repositories {
    mavenCentral()
}

dependencies {
    implementation("org.springframework.boot:spring-boot-starter")
    implementation("org.codehaus.janino:janino")
}

The full code is available in this github repo.

The output

When run in the JVM without any further parameters, the output is as follows but shortened for conciseness:

$ java -jar build/libs/logback-if-else-0.0.1-SNAPSHOT.jar 
INFO --- [main] foo.LogbackIfElseApplication  : Starting LogbackIfElseApplication using Java 17.0.5 with PID 5905
INFO --- [main] foo.LogbackIfElseApplication  : No active profile set, falling back to 1 default profile: "default"
INFO --- [main] foo.LogbackIfElseApplication  : Started LogbackIfElseApplication in 0.946 seconds (process running for 1.352)
INFO --- [main] foo.LogbackIfElseApplication  : Hello Logback!
Hello stdout

When run as a native image, the full output is just

$ build/native/nativeCompile/logback-if-else
Hello stdout

Edit

Thanks to Andy's fix, the application no longer starts up without logging but fails early and logs

Logging system failed to initialize using configuration from 'null'
java.lang.IllegalStateException: Logback configuration error detected: 
ERROR in ch.qos.logback.core.model.processor.conditional.IfModelHandler - Failed to parse condition [property("WITH_DEBUG").equalsIgnoreCase("true")] on line 3 org.codehaus.commons.compiler.InternalCompilerException: SNO: Generated compilation unit does not declare class 'SC'
        at org.springframework.boot.logging.logback.LogbackLoggingSystem.reportConfigurationErrorsIfNecessary(LogbackLoggingSystem.java:261)
        at org.springframework.boot.logging.logback.LogbackLoggingSystem.initializeFromAotGeneratedArtifactsIfPossible(LogbackLoggingSystem.java:212)
        at org.springframework.boot.logging.logback.LogbackLoggingSystem.initialize(LogbackLoggingSystem.java:187)
        at org.springframework.boot.context.logging.LoggingApplicationListener.initializeSystem(LoggingApplicationListener.java:332)
        at org.springframework.boot.context.logging.LoggingApplicationListener.initialize(LoggingApplicationListener.java:298)
        at org.springframework.boot.context.logging.LoggingApplicationListener.onApplicationEnvironmentPreparedEvent(LoggingApplicationListener.java:246)
        at org.springframework.boot.context.logging.LoggingApplicationListener.onApplicationEvent(LoggingApplicationListener.java:223)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:176)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:169)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:143)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:131)
        at org.springframework.boot.context.event.EventPublishingRunListener.multicastInitialEvent(EventPublishingRunListener.java:136)
        at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:81)
        at org.springframework.boot.SpringApplicationRunListeners.lambda$environmentPrepared$2(SpringApplicationRunListeners.java:64)
        at [email protected]/java.lang.Iterable.forEach(Iterable.java:75)
        at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:118)
        at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:112)
        at org.springframework.boot.SpringApplicationRunListeners.environmentPrepared(SpringApplicationRunListeners.java:63)
        at org.springframework.boot.SpringApplication.prepareEnvironment(SpringApplication.java:352)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:303)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1302)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1291)
        at foo.LogbackIfElseApplication.main(LogbackIfElseApplication.java:16)
Exception in thread "main" java.lang.IllegalStateException: java.lang.IllegalStateException: Logback configuration error detected: 
ERROR in ch.qos.logback.core.model.processor.conditional.IfModelHandler - Failed to parse condition [property("WITH_DEBUG").equalsIgnoreCase("true")] on line 3 org.codehaus.commons.compiler.InternalCompilerException: SNO: Generated compilation unit does not declare class 'SC'
        at org.springframework.boot.context.logging.LoggingApplicationListener.initializeSystem(LoggingApplicationListener.java:347)
        at org.springframework.boot.context.logging.LoggingApplicationListener.initialize(LoggingApplicationListener.java:298)
        at org.springframework.boot.context.logging.LoggingApplicationListener.onApplicationEnvironmentPreparedEvent(LoggingApplicationListener.java:246)
        at org.springframework.boot.context.logging.LoggingApplicationListener.onApplicationEvent(LoggingApplicationListener.java:223)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:176)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:169)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:143)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:131)
        at org.springframework.boot.context.event.EventPublishingRunListener.multicastInitialEvent(EventPublishingRunListener.java:136)
        at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:81)
        at org.springframework.boot.SpringApplicationRunListeners.lambda$environmentPrepared$2(SpringApplicationRunListeners.java:64)
        at [email protected]/java.lang.Iterable.forEach(Iterable.java:75)
        at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:118)
        at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:112)
        at org.springframework.boot.SpringApplicationRunListeners.environmentPrepared(SpringApplicationRunListeners.java:63)
        at org.springframework.boot.SpringApplication.prepareEnvironment(SpringApplication.java:352)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:303)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1302)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1291)
        at foo.LogbackIfElseApplication.main(LogbackIfElseApplication.java:16)
Caused by: java.lang.IllegalStateException: Logback configuration error detected: 
ERROR in ch.qos.logback.core.model.processor.conditional.IfModelHandler - Failed to parse condition [property("WITH_DEBUG").equalsIgnoreCase("true")] on line 3 org.codehaus.commons.compiler.InternalCompilerException: SNO: Generated compilation unit does not declare class 'SC'
        at org.springframework.boot.logging.logback.LogbackLoggingSystem.reportConfigurationErrorsIfNecessary(LogbackLoggingSystem.java:261)
        at org.springframework.boot.logging.logback.LogbackLoggingSystem.initializeFromAotGeneratedArtifactsIfPossible(LogbackLoggingSystem.java:212)
        at org.springframework.boot.logging.logback.LogbackLoggingSystem.initialize(LogbackLoggingSystem.java:187)
        at org.springframework.boot.context.logging.LoggingApplicationListener.initializeSystem(LoggingApplicationListener.java:332)
        ... 19 more

Solution

  • At the time of writing, Janino does not work when building a native image of your Spring Boot application. The problem is being tracked in this Spring Boot issue.