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