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

Change access of AsyncLogger constructor back to public #3527

Open
sundaybluesky opened this issue Mar 11, 2025 · 4 comments
Open

Change access of AsyncLogger constructor back to public #3527

sundaybluesky opened this issue Mar 11, 2025 · 4 comments

Comments

@sundaybluesky
Copy link

sundaybluesky commented Mar 11, 2025

AsyncLogger constructor access changed from public to package-private at 2.24.3 code change - #3263 blocked our CustomLogger instances initialization.

We have a requirement to introduce extra enablement checks in AsyncLogger isEnabled methods to control print out logs or not. Actually AsyncLogger is not extension friendly due to internal referenced AsyncLoggerDisruptor.class constructor is private. Before 2.24.3, we've created CustomLogger with the same package org.apache.logging.log4j.core.async as AsyncLogger and use reflection to initialize CustomLogger instances and it is working 7+ years.

Code sample,

package org.apache.logging.log4j.core.async;

public class CustomLogger extends AsyncLogger {

        @Override
	public boolean isEnabled(final Level level, final Marker marker, final String message) {
           if (isCustomEnablementOn) {
               return true;
           }
           return super.isEnabled(level, marker, message);
        }
}

Asks
Change AsyncLogger constructor access back to public and consider make AsyncLogger more extensible, user can extend, override AsyncLogger and create subclass of AsyncLogger instance directly, no limitation on private AsyncLoggerDisruptor.class or others internal referenced classes access.

@ppkarwasz
Copy link
Contributor

@sundaybluesky,

Did you consider using a custom filter instead? Based on your code all you need is a filter like this:

@Plugin(name = "CustomFilter", category = Node.CATEGORY)
public class CustomFilter extends AbstractFilter {

    @PluginFactory
    public static CustomFilter newFilter(
            @PluginAttribute("onMatch") Result onMatch, @PluginAttribute("onMismatch") Result onMismatch) {
        return new CustomFilter(onMatch, onMismatch);
    }

    private CustomFilter(Result onMatch, Result onMismatch) {
        super(onMatch, onMismatch);
    }

    private Result filter() {
        return isCustomEnablementOn ? getOnMatch() : getOnMismatch();
    }

    @Override
    public Result filter(Logger logger, Level level, Marker marker, Message msg, Throwable t) {
        return filter();
    }

    @Override
    public Result filter(Logger logger, Level level, Marker marker, Object msg, Throwable t) {
        return filter();
    }

    // All other methods except `filter(LogEvent)`, which will not be used.
}

You can then use it in your configuration:

<Configuration>
  <CustomFilter onMatch="ACCEPT" onMismatch="NEUTRAL"/>
  <!-- ... -->
  <Loggers>
    <!-- Logger configuration -->
  </Loggers>
</Configuration>

See Filters and Extending filters for more information on how filters work. In your case the filter will work at the Logger stage and behave exactly like your current code.

@ppkarwasz ppkarwasz added waiting-for-user More information is needed from the user and removed waiting-for-maintainer labels Mar 11, 2025
@sundaybluesky
Copy link
Author

@ppkarwasz

Is this CustomFilter run in LogEvent consumer side or producer side? It could introduce performance workload if filter logic is performed at consumer side, as far as I known LogEvent creation is using multiple threads, but consumer is single thread.

@github-actions github-actions bot added waiting-for-maintainer and removed waiting-for-user More information is needed from the user labels Mar 12, 2025
@ppkarwasz
Copy link
Contributor

Is this CustomFilter run in LogEvent consumer side or producer side? It could introduce performance workload if filter logic is performed at consumer side, as far as I known LogEvent creation is using multiple threads, but consumer is single thread.

Yes, the global filter (direct child of the <Configuration> element) is always executed on the producer thread, never on the consumer thread. It is executed before a LogEvent is created:

@Override
public boolean isEnabled(final Level level, final Marker marker, final String message, final Throwable t) {
return privateConfig.filter(level, marker, message, t);
}

boolean filter(final Level level, final Marker marker, final String msg, final Throwable t) {
final Filter filter = config.getFilter();
if (filter != null) {
final Filter.Result r = filter.filter(logger, level, marker, (Object) msg, t);
if (r != Filter.Result.NEUTRAL) {
return r == Filter.Result.ACCEPT;
}
}
return level != null && intLevel >= level.intLevel();
}

Filters can also appear in other parts of a configuration, as children of loggers, appender references or appenders. Those are executed on the consumer thread and have an increasingly higher overhead, especially for messages that are discarded.

@ppkarwasz ppkarwasz added waiting-for-user More information is needed from the user and removed waiting-for-maintainer labels Mar 12, 2025
@sundaybluesky
Copy link
Author

Thanks @ppkarwasz for your information, I'll try the global filter option and do performance test, then update the result.

@github-actions github-actions bot added waiting-for-maintainer and removed waiting-for-user More information is needed from the user labels Mar 13, 2025
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

2 participants