Can you solve your problem by writing a JUnit extension (or `@BeforeAll`/`@AfterAll`) that registers your custom filter to the active logger context and removes it after the test? That is,
@FailOnLog4jEvent(level = Level.WARN, namePrefix = "org.hibernate.") class SomeHibernateTest { // ... `@Test`-annotated methods ... } If this is only one test, you can even make it simpler: class SomeHibernateTest { @BeforeAll static void registerLog4jEventBan() { // ... } @AfterAll static void removeLog4jEventBan() { } // ... `@Test`-annotated methods ... } Maybe it is me, but I am not able to see the necessity to add a new `Result` type, in particular, given you stated *"I only want this check on this one test class"*. On Wed, Oct 9, 2024 at 4:34 PM Gary D. Gregory <ggreg...@apache.org> 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); > } > > } >