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); >> } >> >> } >>