Hi Ralph, I'll try again:
As mentioned, I don't want to have to write this custom filter in the first place. I want to write: @SuppressWarnings("resource") @BeforeAll static void beforeAddFilter() { LoggerContext.getContext(false).getLogger("org.hibernate").addFilter(ThresholdFilter.createFilter(Level.WARN, *Result.THROW*, Result.NEUTRAL)); } The value Result.THROW does not exist today. This proposal is to: - Add Result.THROW - There is no need to modify any Filter impls. - When Core sees Result.THROW result from a Filter, it throws a FilterLoggingException. I hope that helps make my proposal clearer :-) TY, Gary On Wed, Oct 9, 2024 at 2:46 PM Ralph Goers <ralph.go...@dslextreme.com> wrote: > Yeah, you lost me. Your code has > > private Result filter(final Level testLevel) { > if (testLevel.isMoreSpecificThan(this.level)) { > throw new FilterLoggingException(Objects.toString(testLevel)); > } > return onMismatch; > } > > This isn’t returning a new type of Result, it is actually throwing an > exception. If it was doing > return Result.FilterException; > > Instead of the throw then I could understand what you are saying. > > Ralph > > > > On Oct 9, 2024, at 9:55 AM, Gary Gregory <garydgreg...@gmail.com> wrote: > > > > 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); > >>>> } > >>>> > >>>> } > >>>> > >> > >> > >