Skip to content

ComponentLogger does not respect Log4j configuration #1220

@Strokkur424

Description

@Strokkur424

The Issue

(Context primarily written in a Paper plugin environment)

I am loading a log4j2.xml configuration file using this snipped of code:

final URL configUrl = getClass().getResource("/log4j2.xml");
if (configUrl != null) {
    Configurator.initialize("PluginName", configUrl.toExternalForm());
}

The logger retrieved using org.slf4j.LoggerFactory.getLogger respects this configuration correctly, but when retrieving a logger with ComponentLogger.logger, it does not do this. This results in an inconsistency between these two loggers.

For this to happen, I think the service provider for the component loggers needs to be modified to account for the active configuration in the class loader, the same as it is the case for the slf4j logger.

I believe the issue lies with the Java service loading structure, more specifically with the net.kyori.adventure.util.Services class, which may not consider the slf4j provider service, causing issues with the configuration.

Minimal reproduction:

  1. Create a new Paper plugin TestPlugin.

  2. Add a compileOnly("org.apache.logging.log4j", "log4j-core", "2.24.1") dependency to access log4j Configurator code.

  3. Create a resource, log4j2.xml, and paste this into the contents:

    log4j2.xml
    <?xml version="1.0" encoding="UTF-8" ?>
    <Configuration>
        <Appenders>
            <RollingRandomAccessFile name="TestPluginFile"
                                        fileName="plugins/TestPlugin/logs/latest.log"
                                        filePattern="plugins/TestPlugin/logs/%d{yyyy-MM-dd}-%i.log.gz">
                <PatternLayout>
                    <LoggerNamePatternSelector defaultPattern="[%d{HH:mm:ss}] [%t/%level]: [%logger] %msg%n%xEx{full}"/>
                </PatternLayout>
                <Policies>
                    <TimeBasedTriggeringPolicy/>
                    <OnStartupTriggeringPolicy/>
                </Policies>
                <DefaultRolloverStrategy max="5"/>
            </RollingRandomAccessFile>
        </Appenders>
        <Loggers>
            <Root level="INFO"/>
            <Logger name="your.package.name" level="INFO" additivity="true">
                <AppenderRef ref="TestPluginFile" />
            </Logger>
        </Loggers>
    </Configuration>
    
  4. Put this into your plugin main class:

    TestPlugin.java
    package your.package.name;
    
    import net.kyori.adventure.text.logger.slf4j.ComponentLogger;
    import org.apache.logging.log4j.core.config.Configurator;
    import org.bukkit.plugin.java.JavaPlugin;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import java.net.URL;
    
    public final class TestPlugin extends JavaPlugin {
    
        @Override
        public void onLoad() {
            final URL configUrl = getClass().getResource("/log4j2.xml");
            if (configUrl != null) {
                Configurator.initialize("PluginName", this.getClassLoader(), configUrl.toExternalForm());
            }
            else {
                getLogger().severe("Failed to load log4j2.xml logger config file!");
            }
    
            final Logger LOGGER = LoggerFactory.getLogger(TestPlugin.class);
            final Logger COMPONENT_LOGGER = ComponentLogger.logger(TestPlugin.class);
     
            LOGGER.warn("SLF4J Logger: Check!");
            COMPONENT_LOGGER.warn("Component Logger: Broken :(");
        }
    }
    
  5. Now, when running the plugin on a Paper server, Component Logger: Broken :( gets printed into the console, whilst SLF4J Logger: Check! is added into /plugins/TestPlugin/logs/latest.log.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions