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

Reply via email to