This just feels wrong. Filters are for determining whether log events should be 
logged. This Filter doesn’t have anything to do with that. It is more like an 
interceptor. 

Secondly, I don’t like that the only criteria it is able to use to determine 
whether to throw an exception is the log level.  

This just feels way too specific.

Ralph

> On Oct 9, 2024, at 7:36 AM, Gary D. Gregory <ggreg...@apache.org> wrote:
> 
> Typo, the new code would be:
> 
> private static final Filter THROWING_FILTER = 
> ThresholdFilter.createFilter(Level.WARN, Result.THROW, Result.NEUTRAL);
> 
> Sorry about that.
> 
> Gary
> 
> On 2024/10/09 14:34:15 "Gary D. Gregory" wrote:
>> Hi All:
>> 
>> As a user, I want to programmatically configure Log4j to throw an exception 
>> when a specific component logs a WARN event.
>> 
>> Background: I am working with a large and complex stack that include 
>> Hibernate in the mix. I just fixed a bug that, that as a side-effect, caused 
>> Hibernate to log WARN events. As a check on possible regressions, I want to 
>> make sure that a test class fails when Hibernate logs a WARN event. This 
>> whole Maven module shares a log4j configuration file and, for now, I only 
>> want this check on this one test class.
>> 
>> My current implementation uses a custom filter called 
>> ThrowingThresholdFilter [see end of message], a copy of our ThresholdFilter 
>> that throws a subclass of LoggingException called FilterLoggingException 
>> when the configured Level is matched.
>> 
>> I also have happen to have other checks with other custom filters: 
>> ThrowingLevelFilter and ThrowingStringMatchFilter. 
>> 
>> The only change in the configuration file is the use of the 
>> “ignoreExceptions” attribute to a Console Appender.
>> 
>> The test contains:
>> private static final Filter THROWING_FILTER = 
>> ThrowingThresholdFilter.createFilter(Level.WARN, Result.NEUTRAL);
>>    @SuppressWarnings("resource")
>>    @BeforeAll
>>    static void beforeAddFilter() {
>>        
>> LoggerContext.getContext(false).getLogger("org.hibernate").addFilter(THROWING_FILTER);
>>    }
>> 
>> My proposal is to allow a user to _not_ define any custom filters by reusing 
>> a new Result enum value called “Throw”. When a filter returns “Throw”, then 
>> Log4j throws a new LoggingException subclass called FilterLoggingException.
>> 
>> Then my test can replace:
>> 
>> private static final Filter THROWING_FILTER = 
>> ThrowingThresholdFilter.createFilter(Level.WARN, Result.NEUTRAL);
>> 
>> and drop all custom filters.
>> 
>> With:
>> 
>> private static final Filter THROWING_FILTER = 
>> ThresholdFilter.createFilter(Level.WARN, Result.NEUTRAL);
>> 
>> WDYT?
>> 
>> Gary
>> 
>> package my.company;
>> 
>> import java.util.Objects;
>> 
>> import org.apache.logging.log4j.Level;
>> import org.apache.logging.log4j.LoggingException;
>> import org.apache.logging.log4j.Marker;
>> import org.apache.logging.log4j.core.Filter;
>> import org.apache.logging.log4j.core.LogEvent;
>> import org.apache.logging.log4j.core.Logger;
>> import org.apache.logging.log4j.core.config.Node;
>> import org.apache.logging.log4j.core.config.plugins.Plugin;
>> import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
>> import org.apache.logging.log4j.core.config.plugins.PluginFactory;
>> import org.apache.logging.log4j.core.filter.AbstractFilter;
>> import org.apache.logging.log4j.message.Message;
>> import org.apache.logging.log4j.util.PerformanceSensitive;
>> 
>> /**
>> * This filter returns the onMatch result if the level in the {@link 
>> LogEvent} is the same or more specific
>> * than the configured level and the {@code onMismatch} value otherwise. For 
>> example, if the ThresholdFilter
>> * is configured with Level {@code ERROR} and the LogEvent contains Level 
>> {@code DEBUG} then the {@code onMismatch} value will
>> * be returned since {@code ERROR} events are more specific than {@code 
>> DEBUG}.
>> * <p>
>> * The default Level is {@code ERROR}.
>> * </p>
>> *
>> * @see Level#isMoreSpecificThan(Level)
>> */
>> @Plugin(name = "ThrowingThresholdFilter", category = Node.CATEGORY, 
>> elementType = Filter.ELEMENT_TYPE, printObject = true)
>> @PerformanceSensitive("allocation")
>> public final class ThrowingThresholdFilter extends AbstractFilter {
>> 
>>    public static class FilterLoggingException extends LoggingException {
>> 
>>        private static final long serialVersionUID = 1L;
>> 
>>        public FilterLoggingException(String message) {
>>            super(message);
>>        }
>> 
>>    }
>> 
>>    private final Level level;
>> 
>>    private ThrowingThresholdFilter(final Level level, final Result 
>> onMismatch) {
>>        super(Result.NEUTRAL, onMismatch);
>>        this.level = level;
>>    }
>> 
>>    @Override
>>    public Result filter(final Logger logger, final Level testLevel, final 
>> Marker marker, final String msg,
>>                         final Object... params) {
>>        return filter(testLevel);
>>    }
>> 
>>    @Override
>>    public Result filter(final Logger logger, final Level testLevel, final 
>> Marker marker, final Object msg,
>>                         final Throwable t) {
>>        return filter(testLevel);
>>    }
>> 
>>    @Override
>>    public Result filter(final Logger logger, final Level testLevel, final 
>> Marker marker, final Message msg,
>>                         final Throwable t) {
>>        return filter(testLevel);
>>    }
>> 
>>    @Override
>>    public Result filter(final LogEvent event) {
>>        return filter(event.getLevel());
>>    }
>> 
>>    private Result filter(final Level testLevel) {
>>        if (testLevel.isMoreSpecificThan(this.level)) {
>>            throw new FilterLoggingException(Objects.toString(testLevel));
>>        }
>>        return onMismatch;
>>    }
>> 
>>    @Override
>>    public Result filter(final Logger logger, final Level level, final Marker 
>> marker, final String msg,
>>            final Object p0) {
>>        return filter(level);
>>    }
>> 
>>    @Override
>>    public Result filter(final Logger logger, final Level level, final Marker 
>> marker, final String msg,
>>            final Object p0, final Object p1) {
>>        return filter(level);
>>    }
>> 
>>    @Override
>>    public Result filter(final Logger logger, final Level level, final Marker 
>> marker, final String msg,
>>            final Object p0, final Object p1, final Object p2) {
>>        return filter(level);
>>    }
>> 
>>    @Override
>>    public Result filter(final Logger logger, final Level level, final Marker 
>> marker, final String msg,
>>            final Object p0, final Object p1, final Object p2, final Object 
>> p3) {
>>        return filter(level);
>>    }
>> 
>>    @Override
>>    public Result filter(final Logger logger, final Level level, final Marker 
>> marker, final String msg,
>>            final Object p0, final Object p1, final Object p2, final Object 
>> p3,
>>            final Object p4) {
>>        return filter(level);
>>    }
>> 
>>    @Override
>>    public Result filter(final Logger logger, final Level level, final Marker 
>> marker, final String msg,
>>            final Object p0, final Object p1, final Object p2, final Object 
>> p3,
>>            final Object p4, final Object p5) {
>>        return filter(level);
>>    }
>> 
>>    @Override
>>    public Result filter(final Logger logger, final Level level, final Marker 
>> marker, final String msg,
>>            final Object p0, final Object p1, final Object p2, final Object 
>> p3,
>>            final Object p4, final Object p5, final Object p6) {
>>        return filter(level);
>>    }
>> 
>>    @Override
>>    public Result filter(final Logger logger, final Level level, final Marker 
>> marker, final String msg,
>>            final Object p0, final Object p1, final Object p2, final Object 
>> p3,
>>            final Object p4, final Object p5, final Object p6,
>>            final Object p7) {
>>        return filter(level);
>>    }
>> 
>>    @Override
>>    public Result filter(final Logger logger, final Level level, final Marker 
>> marker, final String msg,
>>            final Object p0, final Object p1, final Object p2, final Object 
>> p3,
>>            final Object p4, final Object p5, final Object p6,
>>            final Object p7, final Object p8) {
>>        return filter(level);
>>    }
>> 
>>    @Override
>>    public Result filter(final Logger logger, final Level level, final Marker 
>> marker, final String msg,
>>            final Object p0, final Object p1, final Object p2, final Object 
>> p3,
>>            final Object p4, final Object p5, final Object p6,
>>            final Object p7, final Object p8, final Object p9) {
>>        return filter(level);
>>    }
>> 
>>    public Level getLevel() {
>>        return level;
>>    }
>> 
>>    @Override
>>    public String toString() {
>>        return level.toString();
>>    }
>> 
>>    /**
>>     * Creates a ThrowingThresholdFilter.
>>     * @param level The log Level.
>>     * @param mismatch The action to take on a mismatch.
>>     * @return The created ThrowingThresholdFilter.
>>     */
>>    // TODO Consider refactoring to use AbstractFilter.AbstractFilterBuilder
>>    @PluginFactory
>>    public static ThrowingThresholdFilter createFilter(
>>            @PluginAttribute("level") final Level level,
>>            @PluginAttribute("onMismatch") final Result mismatch) {
>>        final Level actualLevel = level == null ? Level.ERROR : level;
>>        final Result onMismatch = mismatch == null ? Result.DENY : mismatch;
>>        return new ThrowingThresholdFilter(actualLevel, onMismatch);
>>    }
>> 
>> }
>> 

Reply via email to