diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/AbstractFilterTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/AbstractFilterTest.java index 8996b9f2f16..fc42abe2937 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/AbstractFilterTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/AbstractFilterTest.java @@ -34,7 +34,6 @@ class AbstractFilterTest { @Test void testUnrolledBackwardsCompatible() { final ConcreteFilter filter = new ConcreteFilter(); - final Filter.Result expected = Filter.Result.DENY; verifyMethodsWithUnrolledVarargs(filter, Filter.Result.DENY); filter.testResult = Filter.Result.ACCEPT; diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/AbstractFilterableTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/AbstractFilterableTest.java index 4f117a1ccf5..bd3d3f51b20 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/AbstractFilterableTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/AbstractFilterableTest.java @@ -54,7 +54,7 @@ void testAddMultipleSimpleFilters() { // into a CompositeFilter.class filterable.addFilter(filter); assertInstanceOf(CompositeFilter.class, filterable.getFilter()); - assertEquals(2, ((CompositeFilter) filterable.getFilter()).getFilters().size()); + assertEquals(2, ((CompositeFilter) filterable.getFilter()).getFiltersArray().length); } @Test @@ -67,7 +67,7 @@ void testAddMultipleEqualSimpleFilter() { // into a CompositeFilter.class filterable.addFilter(filter); assertInstanceOf(CompositeFilter.class, filterable.getFilter()); - assertEquals(2, ((CompositeFilter) filterable.getFilter()).getFilters().size()); + assertEquals(2, ((CompositeFilter) filterable.getFilter()).getFiltersArray().length); } @Test @@ -93,7 +93,7 @@ void testAddMultipleCompositeFilters() { // into a CompositeFilter.class filterable.addFilter(compositeFilter); assertInstanceOf(CompositeFilter.class, filterable.getFilter()); - assertEquals(6, ((CompositeFilter) filterable.getFilter()).getFilters().size()); + assertEquals(6, ((CompositeFilter) filterable.getFilter()).getFiltersArray().length); } @Test @@ -109,7 +109,7 @@ void testAddSimpleFilterAndCompositeFilter() { // into a CompositeFilter.class filterable.addFilter(compositeFilter); assertInstanceOf(CompositeFilter.class, filterable.getFilter()); - assertEquals(2, ((CompositeFilter) filterable.getFilter()).getFilters().size()); + assertEquals(2, ((CompositeFilter) filterable.getFilter()).getFiltersArray().length); } @Test @@ -125,7 +125,7 @@ void testAddCompositeFilterAndSimpleFilter() { // into a CompositeFilter.class filterable.addFilter(notInCompositeFilterFilter); assertInstanceOf(CompositeFilter.class, filterable.getFilter()); - assertEquals(3, ((CompositeFilter) filterable.getFilter()).getFilters().size()); + assertEquals(3, ((CompositeFilter) filterable.getFilter()).getFiltersArray().length); } @Test @@ -170,7 +170,7 @@ void testRemoveSimpleEqualFilterFromMultipleSimpleFilters() { filterable.addFilter(filterCopy); filterable.removeFilter(filterCopy); assertInstanceOf(CompositeFilter.class, filterable.getFilter()); - assertEquals(2, ((CompositeFilter) filterable.getFilter()).getFilters().size()); + assertEquals(2, ((CompositeFilter) filterable.getFilter()).getFiltersArray().length); filterable.removeFilter(filterCopy); assertEquals(filterOriginal, filterable.getFilter()); filterable.removeFilter(filterOriginal); @@ -224,7 +224,7 @@ void testRemoveSimpleFilterFromCompositeAndSimpleFilter() { // should not remove internal filter of compositeFilter filterable.removeFilter(anotherFilter); assertInstanceOf(CompositeFilter.class, filterable.getFilter()); - assertEquals(2, ((CompositeFilter) filterable.getFilter()).getFilters().size()); + assertEquals(2, ((CompositeFilter) filterable.getFilter()).getFiltersArray().length); } @Test @@ -247,9 +247,9 @@ void testRemoveFiltersFromComposite() { filterable.addFilter(compositeFilter); filterable.addFilter(anotherFilter); - assertEquals(3, ((CompositeFilter) filterable.getFilter()).getFilters().size()); + assertEquals(3, ((CompositeFilter) filterable.getFilter()).getFiltersArray().length); filterable.removeFilter(filter1); - assertEquals(2, ((CompositeFilter) filterable.getFilter()).getFilters().size()); + assertEquals(2, ((CompositeFilter) filterable.getFilter()).getFiltersArray().length); filterable.removeFilter(filter2); assertSame(anotherFilter, filterable.getFilter()); } @@ -274,11 +274,7 @@ public boolean equals(final Object o) { final EqualFilter that = (EqualFilter) o; - if (key != null ? !key.equals(that.key) : that.key != null) { - return false; - } - - return true; + return key != null ? key.equals(that.key) : that.key == null; } @Override diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/RegexFilterTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/RegexFilterTest.java index 671d998258b..3bf96c607fd 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/RegexFilterTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/RegexFilterTest.java @@ -19,6 +19,9 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -37,19 +40,23 @@ class RegexFilterTest { @BeforeAll static void before() { - StatusLogger.getLogger().setLevel(Level.OFF); + StatusLogger.getLogger().getFallbackListener().setLevel(Level.OFF); } @Test void testRegexFilterDoesNotThrowWithAllTheParametersExceptRegexEqualNull() { assertDoesNotThrow(() -> { - RegexFilter.createFilter(".* test .*", null, null, null, null); + RegexFilter.newBuilder().setRegex(".* test .*").build(); }); } @Test void testThresholds() throws Exception { - RegexFilter filter = RegexFilter.createFilter(".* test .*", null, false, null, null); + RegexFilter filter = RegexFilter.newBuilder() + .setRegex(".* test .*") + .setUseRawMsg(false) + .build(); + assertNotNull(filter); filter.start(); assertTrue(filter.isStarted()); assertSame( @@ -65,7 +72,7 @@ void testThresholds() throws Exception { .setMessage(new SimpleMessage("test")) // .build(); assertSame(Filter.Result.DENY, filter.filter(event)); - filter = RegexFilter.createFilter(null, null, false, null, null); + filter = RegexFilter.newBuilder().build(); assertNull(filter); } @@ -82,9 +89,17 @@ void testDotAllPattern() throws Exception { } @Test - void testNoMsg() throws Exception { - final RegexFilter filter = RegexFilter.createFilter(".* test .*", null, false, null, null); + void testNoMsg() { + + final RegexFilter filter = RegexFilter.newBuilder() + .setRegex(".* test .*") + .setUseRawMsg(false) + .build(); + + assertNotNull(filter); + filter.start(); + assertTrue(filter.isStarted()); assertSame(Filter.Result.DENY, filter.filter(null, Level.DEBUG, null, (Object) null, null)); assertSame(Filter.Result.DENY, filter.filter(null, Level.DEBUG, null, (Message) null, null)); @@ -92,28 +107,105 @@ void testNoMsg() throws Exception { } @Test - void testParameterizedMsg() throws Exception { + void testParameterizedMsg() { final String msg = "params {} {}"; final Object[] params = {"foo", "bar"}; // match against raw message - final RegexFilter rawFilter = RegexFilter.createFilter( - "params \\{\\} \\{\\}", - null, - true, // useRawMsg - Result.ACCEPT, - Result.DENY); + final RegexFilter rawFilter = RegexFilter.newBuilder() + .setRegex("params \\{\\} \\{\\}") + .setUseRawMsg(true) + .setOnMatch(Result.ACCEPT) + .setOnMismatch(Result.DENY) + .build(); + + assertNotNull(rawFilter); + final Result rawResult = rawFilter.filter(null, null, null, msg, params); assertThat(rawResult, equalTo(Result.ACCEPT)); // match against formatted message - final RegexFilter fmtFilter = RegexFilter.createFilter( - "params foo bar", - null, - false, // useRawMsg - Result.ACCEPT, - Result.DENY); + final RegexFilter fmtFilter = RegexFilter.newBuilder() + .setRegex("params foo bar") + .setUseRawMsg(false) + .setOnMatch(Result.ACCEPT) + .setOnMismatch(Result.DENY) + .build(); + + assertNotNull(fmtFilter); + final Result fmtResult = fmtFilter.filter(null, null, null, msg, params); assertThat(fmtResult, equalTo(Result.ACCEPT)); } + + /** + * A builder with no 'regex' expression should both be invalid and return null on 'build()'. + */ + @Test + void testWithValidRegex() { + + final String regex = "^[a-zA-Z0-9_]+$"; // matches alphanumeric with underscores + + final RegexFilter.Builder builder = RegexFilter.newBuilder() + .setRegex(regex) + .setUseRawMsg(false) + .setOnMatch(Result.ACCEPT) + .setOnMismatch(Result.DENY); + + final RegexFilter filter = builder.build(); + + assertNotNull(filter); + + assertEquals(Result.ACCEPT, filter.filter("Hello_123")); + + assertEquals(Result.DENY, filter.filter("Hello@123")); + + assertEquals(regex, filter.getRegex()); + } + + @Test + void testRegexFilterGetters() { + + final String regex = "^[a-zA-Z0-9_]+$"; // matches alphanumeric with underscores + + final RegexFilter filter = RegexFilter.newBuilder() + .setRegex(regex) + .setUseRawMsg(false) + .setOnMatch(Result.ACCEPT) + .setOnMismatch(Result.DENY) + .build(); + + assertNotNull(filter); + + assertEquals(regex, filter.getRegex()); + assertFalse(filter.isUseRawMessage()); + assertEquals(Result.ACCEPT, filter.getOnMatch()); + assertEquals(Result.DENY, filter.getOnMismatch()); + assertNotNull(filter.getPattern()); + assertEquals(regex, filter.getPattern().pattern()); + } + + /** + * A builder with no 'regex' expression should both be invalid and return null on 'build()'. + */ + @Test + void testBuilderWithoutRegexNotValid() { + + final RegexFilter.Builder builder = RegexFilter.newBuilder(); + + assertNull(builder.build()); + } + + /** + * A builder with an invalid 'regex' expression should return null on 'build()'. + */ + @Test + void testBuilderWithInvalidRegexNotValid() { + + final RegexFilter.Builder builder = RegexFilter.newBuilder(); + + builder.setRegex("[a-z"); + + assertNull(builder.build()); + } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/AbstractFilter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/AbstractFilter.java index 397390bcbc3..05b1e6b275b 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/AbstractFilter.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/AbstractFilter.java @@ -16,6 +16,8 @@ */ package org.apache.logging.log4j.core.filter; +import java.util.Objects; +import java.util.Optional; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.core.AbstractLifeCycle; @@ -43,16 +45,30 @@ public abstract static class AbstractFilterBuilder builder) { + + Objects.requireNonNull(builder, "The 'builder' argument cannot be null."); + + this.onMatch = Optional.ofNullable(builder.onMatch).orElse(Result.NEUTRAL); + this.onMismatch = Optional.ofNullable(builder.onMismatch).orElse(Result.DENY); + } + @Override protected boolean equalsImpl(final Object obj) { if (this == obj) { diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/RegexFilter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/RegexFilter.java index 62d41b31f59..839089b35bb 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/RegexFilter.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/RegexFilter.java @@ -19,7 +19,7 @@ import java.lang.reflect.Field; import java.util.Arrays; import java.util.Comparator; -import java.util.regex.Matcher; +import java.util.Objects; import java.util.regex.Pattern; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Marker; @@ -29,68 +29,210 @@ 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.PluginBuilderAttribute; +import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory; import org.apache.logging.log4j.core.config.plugins.PluginElement; -import org.apache.logging.log4j.core.config.plugins.PluginFactory; +import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required; +import org.apache.logging.log4j.core.util.Assert; import org.apache.logging.log4j.message.Message; import org.apache.logging.log4j.message.MessageFormatMessage; import org.apache.logging.log4j.message.ParameterizedMessage; import org.apache.logging.log4j.message.StringFormattedMessage; import org.apache.logging.log4j.message.StructuredDataMessage; +import org.apache.logging.log4j.util.Strings; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** - * A filter that matches the given regular expression pattern against messages. + * This filter returns the {@code onMatch} result if the message exactly matches the configured + * "{@code regex}" regular-expression pattern; otherwise, it returns the {@code onMismatch} result. */ @Plugin(name = "RegexFilter", category = Node.CATEGORY, elementType = Filter.ELEMENT_TYPE, printObject = true) +@NullMarked public final class RegexFilter extends AbstractFilter { - private static final int DEFAULT_PATTERN_FLAGS = 0; + /** The pattern compiled from the regular-expression. */ private final Pattern pattern; + + /** Flag: if {@code true} use message format-pattern / field for the match target. */ private final boolean useRawMessage; - private RegexFilter(final boolean raw, final Pattern pattern, final Result onMatch, final Result onMismatch) { - super(onMatch, onMismatch); - this.pattern = pattern; - this.useRawMessage = raw; + /** + * Constructs a new {@code RegexFilter} configured by the given builder. + * @param builder the builder + * @throws IllegalArgumentException if the regular expression is not configured or cannot be compiled to a pattern + */ + private RegexFilter(final Builder builder) { + + super(builder); + + if (Strings.isBlank(builder.regex)) { + throw new IllegalArgumentException("The 'regex' attribute must not be null or empty."); + } + + this.useRawMessage = Boolean.TRUE.equals(builder.useRawMsg); + + try { + this.pattern = Pattern.compile(builder.regex); + } catch (final Exception ex) { + throw new IllegalArgumentException("Unable to compile regular expression: '" + builder.regex + "'.", ex); + } + } + + /** + * Returns the compiled regular-expression pattern. + * @return the pattern (will never be {@code null} + */ + public Pattern getPattern() { + return this.pattern; + } + + /** + * Returns the regular-expression. + * @return the regular-expression (it may be an empty string but never {@code null}) + */ + public String getRegex() { + return this.pattern.pattern(); + } + + /** + * Returns whether the raw-message should be used. + * @return {@code true} if the raw message should be used; otherwise, {@code false} + */ + public boolean isUseRawMessage() { + return this.useRawMessage; } + /** + * {@inheritDoc} + *

+ * This implementation performs the filter evaluation against the given message formatted with + * the given parameters. + *

+ *

+ * The following method arguments are ignored by this filter method implementation: + *

+ *

+ */ @Override public Result filter( - final Logger logger, final Level level, final Marker marker, final String msg, final Object... params) { - if (useRawMessage || params == null || params.length == 0) { - return filter(msg); - } - return filter(ParameterizedMessage.format(msg, params)); + final @Nullable Logger logger, + final @Nullable Level level, + final @Nullable Marker marker, + final @Nullable String msg, + final @Nullable Object @Nullable ... params) { + + return (useRawMessage || params == null || params.length == 0) + ? filter(msg) + : filter(ParameterizedMessage.format(msg, params)); } + /** + * {@inheritDoc} + *

+ * This implementation performs the filter evaluation against the given message. + *

+ *

+ * The following method arguments are ignored by this filter method implementation: + *

+ *

+ */ @Override public Result filter( - final Logger logger, final Level level, final Marker marker, final Object msg, final Throwable t) { - if (msg == null) { - return onMismatch; - } - return filter(msg.toString()); + final @Nullable Logger logger, + final @Nullable Level level, + final @Nullable Marker marker, + final @Nullable Object message, + final @Nullable Throwable throwable) { + + return (message == null) ? this.onMismatch : filter(message.toString()); } + /** + * {@inheritDoc} + *

+ * This implementation performs the filter evaluation against the given message. + *

+ *

+ * The following method arguments are ignored by this filter method implementation: + *

+ *

+ */ @Override public Result filter( - final Logger logger, final Level level, final Marker marker, final Message msg, final Throwable t) { - if (msg == null) { - return onMismatch; - } - final String text = targetMessageTest(msg); - return filter(text); + final @Nullable Logger logger, + final @Nullable Level level, + final @Nullable Marker marker, + final @Nullable Message message, + final @Nullable Throwable throwable) { + return (message == null) ? this.onMismatch : filter(getMessageTextByType(message)); } + /** + * {@inheritDoc} + * + * @throws NullPointerException if the {@code event} argument is {@code null} + */ @Override public Result filter(final LogEvent event) { - final String text = targetMessageTest(event.getMessage()); - return filter(text); + Objects.requireNonNull(event, "The 'event' argument must not be null."); + return filter(getMessageTextByType(event.getMessage())); + } + + /** + * Apply the filter to the given message and return the {@code onMatch} result if the entire + * message matches the configured regex pattern; otherwise, {@code onMismatch}. + *

+ * If the given '{@code msg}' is {@code null} the configured {@code onMismatch} result will be returned. + *

+ * @param msg the message + * @return the {@code onMatch} result if the pattern matches; otherwise, the {@code onMismatch} result + */ + public Result filter(final @Nullable String msg) { + return (msg != null && pattern.matcher(msg).matches()) ? onMatch : onMismatch; } - // While `Message#getFormat()` is broken in general, it still makes sense for certain types. - // Hence, suppress the deprecation warning. + /** + * Tests the filter pattern against the given Log4j {@code Message}. + *

+ * If the raw-message flag is enabled and message is an instance of the following, the raw message format + * will be returned. + *

+ * + *

+ * If the '{@code useRawMessage}' flag is disabled OR the message is not one of the above + * implementations, the message's formatted message will be returned. + *

+ *

Developer Note

+ *

+ * While `Message#getFormat()` is broken in general, it still makes sense for certain types. + * Hence, suppress the deprecation warning. + *

+ * + * @param message the message + * @return the target message based on configuration and message-type + */ @SuppressWarnings("deprecation") - private String targetMessageTest(final Message message) { + private String getMessageTextByType(final Message message) { return useRawMessage && (message instanceof ParameterizedMessage || message instanceof StringFormattedMessage @@ -100,59 +242,191 @@ private String targetMessageTest(final Message message) { : message.getFormattedMessage(); } - private Result filter(final String msg) { - if (msg == null) { - return onMismatch; + /** {@inheritDoc} */ + @Override + public String toString() { + return "useRawMessage=" + useRawMessage + ", pattern=" + pattern.toString(); + } + + /** + * Creates a new builder instance. + * @return the new builder instance + */ + @PluginBuilderFactory + public static Builder newBuilder() { + return new Builder(); + } + + /** + * A {@link RegexFilter} builder instance. + */ + public static final class Builder extends AbstractFilterBuilder + implements org.apache.logging.log4j.core.util.Builder { + + /* NOTE: LOG4J-3086 - No patternFlags in builder - this functionality has been deprecated/removed. */ + + /** + * The regular expression to match. + */ + @PluginBuilderAttribute + @Required(message = "No 'regex' provided for RegexFilter") + private @Nullable String regex; + + /** + * If {@code true}, for {@link ParameterizedMessage}, {@link StringFormattedMessage}, + * and {@link MessageFormatMessage}, the message format pattern; for {@link StructuredDataMessage}, + * the message field will be used as the match target. + */ + @PluginBuilderAttribute + private @Nullable Boolean useRawMsg; + + /** Private constructor. */ + private Builder() { + super(); + } + + /** + * Sets the regular-expression. + * + * @param regex the regular-expression + * @return this builder + */ + public Builder setRegex(final String regex) { + this.regex = Assert.requireNonEmpty(regex, "The 'regex' attribute must not be null or empty."); + return this; + } + + /** + * Sets the use raw msg flag. + * + * @param useRawMsg {@code true} if the message format-patter/field will be used as match target; + * otherwise, {@code false} + * @return this builder + */ + public Builder setUseRawMsg(final boolean useRawMsg) { + this.useRawMsg = useRawMsg; + return this; + } + + /** {@inheritDoc} */ + @Override + public boolean isValid() { + return (Strings.isNotEmpty(this.regex)); + } + + /** + * Builds and returns a {@link RegexFilter} instance configured by this builder. + * + * @return the created {@link RegexFilter} or {@code null} if the builder is misconfigured + */ + @Override + public @Nullable RegexFilter build() { + + // validate the "regex" attribute + if (Strings.isEmpty(this.regex)) { + LOGGER.error("Unable to create RegexFilter: The 'regex' attribute be set to a non-empty String."); + return null; + } + + // build with *safety* to not throw exceptions + try { + return new RegexFilter(this); + } catch (final Exception ex) { + LOGGER.error("Unable to create RegexFilter. {}", ex.getMessage(), ex); + return null; + } } - final Matcher m = pattern.matcher(msg); - return m.matches() ? onMatch : onMismatch; } - @Override - public String toString() { - final StringBuilder sb = new StringBuilder(); - sb.append("useRaw=").append(useRawMessage); - sb.append(", pattern=").append(pattern.toString()); - return sb.toString(); + /* + * DEPRECATIONS: + * The constructor/fields/methods below have been deprecated. + * - the 'create***' factory methods should no longer be used - use the builder instead + * - pattern-flags should now be passed via the regular expression itself + */ + + /** + * @deprecated pattern flags have been deprecated - they can just be included in the regex-expression. + */ + @Deprecated + private static final int DEFAULT_PATTERN_FLAGS = 0; + + /** + * @deprecated - pattern flags no longer supported. + */ + @Deprecated + private String[] patternFlags = new String[0]; + + /** + * @deprecated use {@link RegexFilter.Builder} instead + */ + @Deprecated + @SuppressWarnings("MagicConstant") + private RegexFilter( + final String regex, + final boolean useRawMessage, + final @Nullable String @Nullable [] patternFlags, + final @Nullable Result onMatch, + final @Nullable Result onMismatch) { + super(onMatch, onMismatch); + Objects.requireNonNull(regex, "The 'regex' argument must be provided for RegexFilter"); + this.patternFlags = patternFlags == null ? new String[0] : patternFlags.clone(); + try { + int flags = toPatternFlags(this.patternFlags); + this.pattern = Pattern.compile(regex, flags); + } catch (final Exception ex) { + throw new IllegalArgumentException("Unable to compile regular expression: '" + regex + "'.", ex); + } + this.useRawMessage = useRawMessage; + } + + /** + * Returns the pattern-flags applied to the regular-expression when compiling the pattern. + * + * @return the pattern-flags (maybe empty but never {@code null} + * @deprecated pattern-flags are no longer supported + */ + @Deprecated + public String[] getPatternFlags() { + return this.patternFlags.clone(); } /** * Creates a Filter that matches a regular expression. * - * @param regex - * The regular expression to match. - * @param patternFlags - * An array of Strings where each String is a {@link Pattern#compile(String, int)} compilation flag. - * @param useRawMsg - * If {@code true}, for {@link ParameterizedMessage}, {@link StringFormattedMessage}, and {@link MessageFormatMessage}, the message format pattern; for {@link StructuredDataMessage}, the message field will be used as the match target. - * @param match - * The action to perform when a match occurs. - * @param mismatch - * The action to perform when a mismatch occurs. + * @param regex The regular expression to match. + * @param patternFlags An array of Strings where each String is a {@link Pattern#compile(String, int)} compilation flag. + * (no longer used - pattern flags can be embedded in regex-expression. + * @param useRawMsg If {@code true}, for {@link ParameterizedMessage}, {@link StringFormattedMessage}, + * and {@link MessageFormatMessage}, the message format pattern; for {@link StructuredDataMessage}, + * the message field will be used as the match target. + * @param match The action to perform when a match occurs. + * @param mismatch The action to perform when a mismatch occurs. * @return The RegexFilter. - * @throws IllegalAccessException When there is no access to the definition of the specified member. + * @throws IllegalAccessException When there is no access to the definition of the specified member. * @throws IllegalArgumentException When passed an illegal or inappropriate argument. + * @deprecated use {@link #newBuilder} to instantiate builder */ - // TODO Consider refactoring to use AbstractFilter.AbstractFilterBuilder - @PluginFactory + @Deprecated public static RegexFilter createFilter( // @formatter:off @PluginAttribute("regex") final String regex, - @PluginElement("PatternFlags") final String[] patternFlags, - @PluginAttribute("useRawMsg") final Boolean useRawMsg, - @PluginAttribute("onMatch") final Result match, - @PluginAttribute("onMismatch") final Result mismatch) + @PluginElement("PatternFlags") final String @Nullable [] patternFlags, + @PluginAttribute("useRawMsg") final @Nullable Boolean useRawMsg, + @PluginAttribute("onMatch") final @Nullable Result match, + @PluginAttribute("onMismatch") final @Nullable Result mismatch) // @formatter:on throws IllegalArgumentException, IllegalAccessException { - if (regex == null) { - LOGGER.error("A regular expression must be provided for RegexFilter"); - return null; - } - return new RegexFilter( - Boolean.TRUE.equals(useRawMsg), Pattern.compile(regex, toPatternFlags(patternFlags)), match, mismatch); + + // LOG4J-3086 - pattern-flags can be embedded in RegEx expression + Objects.requireNonNull(regex, "The 'regex' argument must not be null."); + + return new RegexFilter(regex, Boolean.TRUE.equals(useRawMsg), patternFlags, match, mismatch); } - private static int toPatternFlags(final String[] patternFlags) + /** @deprecated pattern flags have been deprecated - they can just be included in the regex-expression. */ + @Deprecated + private static int toPatternFlags(final String @Nullable [] patternFlags) throws IllegalArgumentException, IllegalAccessException { if (patternFlags == null || patternFlags.length == 0) { return DEFAULT_PATTERN_FLAGS; diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Builder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Builder.java index 10bc1a9f52e..8a772dc7330 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Builder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Builder.java @@ -44,6 +44,10 @@ public interface Builder { */ T build(); + /** + * Validates that the builder is properly configured to build. + * @return {@code true} if the builder configuration is valid; otherwise, {@code false} + */ default boolean isValid() { return PluginBuilder.validateFields(this, getErrorPrefix()); } diff --git a/src/changelog/.2.x.x/3086_change_RegexFilter_remove_patternflags_from_PluginFactory.xml b/src/changelog/.2.x.x/3086_change_RegexFilter_remove_patternflags_from_PluginFactory.xml new file mode 100644 index 00000000000..0e61653f85c --- /dev/null +++ b/src/changelog/.2.x.x/3086_change_RegexFilter_remove_patternflags_from_PluginFactory.xml @@ -0,0 +1,10 @@ + + + + + Removed 'patternFlags' @PluginAttribute from RegexFilter @PluginFactory createFilter. + +