Hi Ralph,

Thank you for taking a peek.

I either did not explain myself clearly or in enough detail as I don't feel
you fully grasped what I proposed ;-)

"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."

It is _not_ only the log level, this would work with _any_ filter. I am
_not_ proposing to add any _new_ filters, that's what I want to avoid. I
propose the ability for any filter to be configured with "Result.THROW", a
new Result value, which log4j-core would translate to throwing an exception.

"Filters are for determining whether log events should be logged ... It is
more like an interceptor."

Yes, this proposal can make a filter look like an interceptor or an
"interruptor" in this case.

Let me present it slightly differently:

I feel it's reasonable for Log4j to provide the ability to cause a unit
test to fail if a log event meets a criterion. JUnit detects exceptions as
test failures. Log4j can propagate exceptions if configured to do so. The
last step missing is how to configure Log4j to throw an exception when a
log event criterion is met. Log4j provides filters which is the closest
feature for testing log events for criteria. So, it seems to me like
filters are the best place to implement this feature. I don't think we need
to invent something else.

HTH,
Gary


On Wed, Oct 9, 2024 at 11:11 AM Ralph Goers <ralph.go...@dslextreme.com>
wrote:

> 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