Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AbstractConfiguration - Potential NPE on AsyncLoggerConfigDisruptor #3156

Open
JWT007 opened this issue Nov 3, 2024 · 1 comment
Open

AbstractConfiguration - Potential NPE on AsyncLoggerConfigDisruptor #3156

JWT007 opened this issue Nov 3, 2024 · 1 comment

Comments

@JWT007
Copy link
Contributor

JWT007 commented Nov 3, 2024

AbstractConfiguration (Log4j 2.24.1)

In AbstractConfiguration the field asyncLoggerConfigDisruptoris lazily initialized.

private AsyncLoggerConfigDisruptor asyncLoggerConfigDisruptor;

public AsyncLoggerConfigDelegate getAsyncLoggerConfigDelegate() {
    // lazily instantiate only when requested by AsyncLoggers:
    // loading AsyncLoggerConfigDisruptor requires LMAX Disruptor jar on classpath
    if (asyncLoggerConfigDisruptor == null) {
        asyncLoggerConfigDisruptor = new AsyncLoggerConfigDisruptor(asyncWaitStrategyFactory);
    }
    return asyncLoggerConfigDisruptor;
}

The method getAsyncLoggerConfigDelegate() would normally be called in the constructor of AsyncLoggerConfig(...):

protected AsyncLoggerConfig(
            final String name,
            final List<AppenderRef> appenders,
            final Filter filter,
            final Level level,
            final boolean additive,
            final Property[] properties,
            final Configuration config,
            final boolean includeLocation) {
        super(name, appenders, filter, level, additive, properties, config, includeLocation);
        delegate = config.getAsyncLoggerConfigDelegate();
        delegate.setLogEventFactory(getLogEventFactory());
    }

Here an assumption is being made that Configuration#getAsyncLoggerConfigDelegate() will not return null and properly initialiize the field, but since Configuration is an interface that can be implemented by anyone, anything is possible. :) It also assumes that the AsyncLoggerConfig has been created with this AbstractConfiguration which may not be a given if created somewhere else and just added with Configuration#addLogger(String, LoggerConfig).

This also implies that the AbstractConfiguration has a certain "knowledge" of the inner workings of the AsyncLoggerConfig which I believe violates some principle of encapsulation. :)

The potential NPE comes up in the AbstractConfiguration#start() and AbstractConfiguration#stop() methods where it is assumed that the asyncLoggerConfigDisruptor has already been lazily initialized to a non-null value.

start()

if (hasAsyncLoggers()) {
    asyncLoggerConfigDisruptor.start();
}

stop()

if (hasAsyncLoggers()) {
    LOGGER.trace("{} stopping AsyncLoggerConfigDisruptor.", cls);
    asyncLoggerConfigDisruptor.stop(timeout, timeUnit);
}

A potential fix is either doing a simple null-check or maybe adding a protected API (since you can't call start() on the AsyncLoggerConfigDelegate:

@Override
public AsyncLoggerConfigDelegate getAsyncLoggerConfigDelegate() {
   return getAsyncLoggerConfigDisruptor();
}

protected AsyncLoggerConfigDisruptor getAsyncLoggerConfigDisruptor() {
    // lazily instantiate only when requested by AsyncLoggers:
    // loading AsyncLoggerConfigDisruptor requires LMAX Disruptor jar on classpath
    if (asyncLoggerConfigDisruptor == null) {
        asyncLoggerConfigDisruptor = new AsyncLoggerConfigDisruptor(asyncWaitStrategyFactory);
    }
    return asyncLoggerConfigDisruptor;
}

public void start() {
    ...
    if (hasAsyncLoggers()) {
        getAsyncLoggerConfigDisruptor().start();
    }
    ...
}

public void stop() {
    ...
    if (hasAsyncLoggers()) {
        getAsyncLoggerConfigDisruptor().stop(timeout, timeUnit);
    }
    ...
}
@JWT007
Copy link
Contributor Author

JWT007 commented Nov 4, 2024

NOTE: additionally I think the constructor will throw an exception if the LMX Jar is not on the classpath (at the very latest in start/stop) but I am not sure how exactly it expresses itselff :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant