0

I have a logger that has the following properties:

PrivateConfig [loggerConfig=Special, config=org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration@709a148, loggerConfigLevel=INFO, intLevel=400, logger=Special.com:INFO in 73d16e93]

This is a Special logger that has a specific format and specific destination needed for my application

This Special logger is created through:

        Level level = Level.toLevel(logLevel);

        ConfigurationBuilder<BuiltConfiguration> builder = ConfigurationBuilderFactory.newConfigurationBuilder();
        builder.setStatusLevel(level);
        builder.setConfigurationName("whatever");

        LoggerComponentBuilder specialLogger = builder.newLogger("Special", level);
        AppenderComponentBuilder specialAppenderBuilder =  builder
                    .newAppender("Special", "RollingFile")
                    .otherComponents();
        specialAppenderBuilder.add(
              builder
                    .newLayout("PatternLayout")
                    .otherComponents();
        builder.add(specialAppenderBuilder);
        specialLogger = specialLogger
              .add(builder.newAppenderRef("Special"))
              .addAttribute("additivity", false);
        builder.add(specialLogger);

        LoggerContext ctx = Configurator.initialize(builder.build());
        ctx.start();

This logger work in Application A, which is not using Spring Boot

I import part of Application A to Application B, which is Spring Boot based, but then the exact same logger become:

PrivateConfig [loggerConfig=root, config=XmlConfiguration[location=jar:file:/Users/stamaki/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot/2.4.4/38392ae406009e55efe873baee4633bfa6b766b3/spring-boot-2.4.4.jar!/org/springframework/boot/logging/log4j2/log4j2.xml], loggerConfigLevel=INFO, intLevel=400, logger=Special.com:INFO in 18b4aac2]

Note that the logger config is being overwritten from Special to root, it no longer follows my custom written BuiltConfiguration but use something from Springboot's internal xml.

How do I stop Spring Boot from overwriting my config?

There appears to be a similar question: Spring is resetting my logging configuration - how do I work around this? But the solution doesn't work

I also tried Spring Boot: LoggingApplicationListener interfering with Application Server logging But their solution still don't work for me as it change my logger into

PrivateConfig [loggerConfig=root, config=org.springframework.boot.logging.log4j2.SpringBootConfigurationFactory$SpringBootConfiguration@16a3cc88, loggerConfigLevel=ERROR, intLevel=200, logger=NonSensitive.com.textiq.precisionenhancer.service.PrecisionEnhancerBackendService:ERROR in 18b4aac2]

i.e. still use Root Config, not the Special Config I set up, only different from above being that this is the default Root Logger config.

Please do not make suggestions like "Update XML so it has the same format", this is not modular enough. I just wanted to use the same logger created by my library without having it being overwritten by Spring Boot.

Tamaki Sakura
  • 482
  • 5
  • 22
  • How do you setup the `LoggerConfig` for your `special.com` logger? If you want to have the same `LoggerContext` as the rest of the application, you need to apply your modifications **after** Spring Boot changed the `Configuration`. Alternatively you can use a separate `LoggerContext`. – Piotr P. Karwasz Jan 14 '22 at 05:24
  • The LoggerConfig is set up by code, I will share a code snippet later. – Tamaki Sakura Jan 14 '22 at 17:20
  • I tried create a different LoggerContext but it also resulted in with logger config overwritten by Spring. – Tamaki Sakura Jan 14 '22 at 17:21

1 Answers1

0

Figure out what happened.

        LoggerContext ctx = Configurator.initialize(builder.build());
        ctx.start();

In both Application A and B, builder.build() has the right information, but Application B's ctx does not have the custom configuration

Investigate inside Configurator.initalize, this attempts to trigger Log4jContextFactory:

    public LoggerContext getContext(final String fqcn, final ClassLoader loader, final Object externalContext,
            final boolean currentContext, final Configuration configuration) {
        final LoggerContext ctx = selector.getContext(fqcn, loader, currentContext, null);
        if (externalContext != null && ctx.getExternalContext() == null) {
            ctx.setExternalContext(externalContext);
        }
        if (ctx.getState() == LifeCycle.State.INITIALIZED) {
            ContextAnchor.THREAD_CONTEXT.set(ctx);
            try {
                ctx.start(configuration);
            } finally {
                ContextAnchor.THREAD_CONTEXT.remove();
            }
        }
        return ctx;
    }

Note that the configuration is not used unless the context currently has state INITIALIZED

Probably because Spring will initialize some sort of Log4j internally in either way, even if you set -Dorg.springframework.boot.logging.LoggingSystem=none, the returned ctx is always STARTED.

The hack to it is that always call reinitailize to force configuration being updated to whatever that's inside builder.build():

        BuiltConfiguration configuration = builder.build();
        LoggerContext ctx = Configurator.initialize(configuration);
        ctx.reconfigure(configuration);
        ctx.start();
Tamaki Sakura
  • 482
  • 5
  • 22