Issue

When using Spring Boot Lookup for Log4j2, java.lang.IllegalStateException: Unable to obtain Spring Environment from LoggerContext get thrown a few times per property per unit test. This is the case not only for tests as well as the web-app.

As I'm using Maven to build the web-app, each maven build contains multiple stacktraces for tests until org.springframework.boot.logging.log4j2.SpringEnvironmentLookup#environment is initialised. Hence, this exception pollutes the app/build logs.

Suggestion

The assertion in SpringEnvironmentLookup#lookup should ideally be a warning log.

Usage

pom.xml

        <properties>
            <java.version>21</java.version>
            <kotlin.version>2.0.0</kotlin.version> <!-- My app is in Kotlin but the issue is present for java too -->
            <kotlin.compiler.jvmTarget>${java.version}</kotlin.compiler.jvmTarget>
        </properties>

        <!-- Spring Boot -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>3.2.6</version>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
            <version>3.2.6</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <version>3.2.6</version>
            <scope>test</scope>
        </dependency>

        <!-- Log4j2 -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.23.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.23.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api-kotlin</artifactId>
            <version>1.4.0</version>
        </dependency>

        <!-- Even added this as per the suggestion on https://logging.apache.org/log4j/2.x/manual/lookups.html#spring-boot-lookup -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-spring-cloud-config-client</artifactId>
            <version>2.23.1</version>
            <scope>runtime</scope>
        </dependency>

log4j2.yml

---
Configuration:
  monitorInterval: 60
  Properties:
    Property:
      - name: appName
        value: "$${spring:spring.application.name:-}" # From application.yml
      - name: myProp
        value: "$${spring:application.my.property:-}" # From application.yml
  Appenders:
    Console:
      name: CONSOLE
      target: SYSTEM_OUT
      PatternLayout:
        pattern: "[app=${appName}][myProp=${myProp}] %d{ISO8601} [%-5p] [%t] [%c]%notEmpty{[%marker]} - %m%n"
  Loggers:
    Logger:
      - name: org.springframework
        level: INFO
        additivity: false
        AppenderRef:
          ref: CONSOLE
    Root:
      level: INFO
      AppenderRef:
        ref: CONSOLE

Exception

2024-09-17T10:29:21.475587700Z main ERROR Resolver failed to lookup spring:spring.application.name java.lang.IllegalStateException: Unable to obtain Spring Environment from LoggerContext
    at org.springframework.util.Assert.state(Assert.java:76)
    at org.springframework.boot.logging.log4j2.SpringEnvironmentLookup.lookup(SpringEnvironmentLookup.java:46)
    at org.apache.logging.log4j.core.lookup.StrLookup.evaluate(StrLookup.java:108)
    at org.apache.logging.log4j.core.lookup.Interpolator.evaluate(Interpolator.java:197)
    at org.apache.logging.log4j.core.lookup.StrSubstitutor.resolveVariable(StrSubstitutor.java:1227)
    at org.apache.logging.log4j.core.lookup.StrSubstitutor.substitute(StrSubstitutor.java:1138)
    at org.apache.logging.log4j.core.lookup.StrSubstitutor.substitute(StrSubstitutor.java:1149)
    at org.apache.logging.log4j.core.lookup.StrSubstitutor.substitute(StrSubstitutor.java:999)
    at org.apache.logging.log4j.core.lookup.StrSubstitutor.replace(StrSubstitutor.java:513)
    at org.apache.logging.log4j.core.config.plugins.visitors.PluginBuilderAttributeVisitor.visit(PluginBuilderAttributeVisitor.java:46)
    at org.apache.logging.log4j.core.config.plugins.util.PluginBuilder.injectFields(PluginBuilder.java:195)
    at org.apache.logging.log4j.core.config.plugins.util.PluginBuilder.build(PluginBuilder.java:123)
    at org.apache.logging.log4j.core.config.AbstractConfiguration.createPluginObject(AbstractConfiguration.java:1164)
    at org.apache.logging.log4j.core.config.AbstractConfiguration.createConfiguration(AbstractConfiguration.java:1085)
    at org.apache.logging.log4j.core.config.AbstractConfiguration.createConfiguration(AbstractConfiguration.java:1077)
    at org.apache.logging.log4j.core.config.AbstractConfiguration.createConfiguration(AbstractConfiguration.java:1077)
    at org.apache.logging.log4j.core.config.AbstractConfiguration.doConfigure(AbstractConfiguration.java:681)
    at org.apache.logging.log4j.core.config.AbstractConfiguration.initialize(AbstractConfiguration.java:264)
    at org.apache.logging.log4j.core.config.AbstractConfiguration.start(AbstractConfiguration.java:313)
    at org.apache.logging.log4j.core.LoggerContext.setConfiguration(LoggerContext.java:631)
    at org.apache.logging.log4j.core.LoggerContext.reconfigure(LoggerContext.java:713)
    at org.apache.logging.log4j.core.LoggerContext.reconfigure(LoggerContext.java:735)
    at org.apache.logging.log4j.core.LoggerContext.start(LoggerContext.java:260)
    at org.apache.logging.log4j.core.impl.Log4jContextFactory.getContext(Log4jContextFactory.java:154)
    at org.apache.logging.log4j.core.impl.Log4jContextFactory.getContext(Log4jContextFactory.java:46)
    at org.apache.logging.log4j.LogManager.getContext(LogManager.java:197)
    at org.apache.commons.logging.LogAdapter$Log4jLog.<clinit>(LogAdapter.java:146)
    at org.apache.commons.logging.LogAdapter$Log4jAdapter.createLog(LogAdapter.java:113)
    at org.apache.commons.logging.LogAdapter.createLog(LogAdapter.java:95)
    at org.apache.commons.logging.LogFactory.getLog(LogFactory.java:67)
    at org.apache.commons.logging.LogFactory.getLog(LogFactory.java:59)
    at org.springframework.test.context.TestContextManager.<clinit>(TestContextManager.java:93)

2024-09-17T10:29:21.480587600Z main ERROR Resolver failed to lookup spring:application.my.property java.lang.IllegalStateException: Unable to obtain Spring Environment from LoggerContext
    at org.springframework.util.Assert.state(Assert.java:76)
    at org.springframework.boot.logging.log4j2.SpringEnvironmentLookup.lookup(SpringEnvironmentLookup.java:46)
    at org.apache.logging.log4j.core.lookup.StrLookup.evaluate(StrLookup.java:108)
    at org.apache.logging.log4j.core.lookup.Interpolator.evaluate(Interpolator.java:197)
    at org.apache.logging.log4j.core.lookup.StrSubstitutor.resolveVariable(StrSubstitutor.java:1227)
    at org.apache.logging.log4j.core.lookup.StrSubstitutor.substitute(StrSubstitutor.java:1138)
    at org.apache.logging.log4j.core.lookup.StrSubstitutor.substitute(StrSubstitutor.java:1149)
    at org.apache.logging.log4j.core.lookup.StrSubstitutor.substitute(StrSubstitutor.java:999)
    at org.apache.logging.log4j.core.lookup.StrSubstitutor.replace(StrSubstitutor.java:513)
    at org.apache.logging.log4j.core.config.plugins.visitors.PluginBuilderAttributeVisitor.visit(PluginBuilderAttributeVisitor.java:46)
    at org.apache.logging.log4j.core.config.plugins.util.PluginBuilder.injectFields(PluginBuilder.java:195)
    at org.apache.logging.log4j.core.config.plugins.util.PluginBuilder.build(PluginBuilder.java:123)
    at org.apache.logging.log4j.core.config.AbstractConfiguration.createPluginObject(AbstractConfiguration.java:1164)
    at org.apache.logging.log4j.core.config.AbstractConfiguration.createConfiguration(AbstractConfiguration.java:1085)
    at org.apache.logging.log4j.core.config.AbstractConfiguration.createConfiguration(AbstractConfiguration.java:1077)
    at org.apache.logging.log4j.core.config.AbstractConfiguration.createConfiguration(AbstractConfiguration.java:1077)
    at org.apache.logging.log4j.core.config.AbstractConfiguration.doConfigure(AbstractConfiguration.java:681)
    at org.apache.logging.log4j.core.config.AbstractConfiguration.initialize(AbstractConfiguration.java:264)
    at org.apache.logging.log4j.core.config.AbstractConfiguration.start(AbstractConfiguration.java:313)
    at org.apache.logging.log4j.core.LoggerContext.setConfiguration(LoggerContext.java:631)
    at org.apache.logging.log4j.core.LoggerContext.reconfigure(LoggerContext.java:713)
    at org.apache.logging.log4j.core.LoggerContext.reconfigure(LoggerContext.java:735)
    at org.apache.logging.log4j.core.LoggerContext.start(LoggerContext.java:260)
    at org.apache.logging.log4j.core.impl.Log4jContextFactory.getContext(Log4jContextFactory.java:154)
    at org.apache.logging.log4j.core.impl.Log4jContextFactory.getContext(Log4jContextFactory.java:46)
    at org.apache.logging.log4j.LogManager.getContext(LogManager.java:197)
    at org.apache.commons.logging.LogAdapter$Log4jLog.<clinit>(LogAdapter.java:146)
    at org.apache.commons.logging.LogAdapter$Log4jAdapter.createLog(LogAdapter.java:113)
    at org.apache.commons.logging.LogAdapter.createLog(LogAdapter.java:95)
    at org.apache.commons.logging.LogFactory.getLog(LogFactory.java:67)
    at org.apache.commons.logging.LogFactory.getLog(LogFactory.java:59)
    at org.springframework.test.context.TestContextManager.<clinit>(TestContextManager.java:93)

Comment From: wilkinsona

As noted in the documentation, Spring Boot's Log4j extensions should only be used in a log4j2-spring.xml file. Doing so prevents Log4j from trying to access the Spring environment before it's available.

Can you please try renaming log4j2.xml to log4j2-spring.xml?

Comment From: RGB314

I'm using YML and it worked after adding logging.config: classpath:log4j2-spring.yml in application.yml. Thanks!

Comment From: wilkinsona

Thanks for letting us know.