This is an automated email from the ASF dual-hosted git repository.
vy pushed a commit to branch 2.x
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git
The following commit(s) were added to refs/heads/2.x by this push:
new 2ca8348810 Remove the `patternFlags` attribute from `RegexFilter`
(#4119)
2ca8348810 is described below
commit 2ca83488108ba2c14802cbbcb2c1e08874d83cc4
Author: Ramanathan <[email protected]>
AuthorDate: Thu Jun 11 21:37:56 2026 +0530
Remove the `patternFlags` attribute from `RegexFilter` (#4119)
Co-authored-by: Jeff Thomas <[email protected]>
Co-authored-by: Volkan Yazıcı <[email protected]>
---
.../log4j/core/filter/AbstractFilterTest.java | 1 -
.../log4j/core/filter/AbstractFilterableTest.java | 24 +-
.../logging/log4j/core/filter/RegexFilterTest.java | 133 +++++--
.../logging/log4j/core/filter/AbstractFilter.java | 28 ++
.../logging/log4j/core/filter/RegexFilter.java | 393 ++++++++++++++++-----
.../logging/log4j/core/filter/package-info.java | 2 +-
.../apache/logging/log4j/core/util/Builder.java | 4 +
.../.2.x.x/regexfilter_patternflags_builder.xml | 15 +
8 files changed, 469 insertions(+), 131 deletions(-)
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 8996b9f2f1..fc42abe293 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 4f117a1ccf..bd3d3f51b2 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 @@ class AbstractFilterableTest {
// 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 @@ class AbstractFilterableTest {
// 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 @@ class AbstractFilterableTest {
// 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 @@ class AbstractFilterableTest {
// 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 @@ class AbstractFilterableTest {
// 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 @@ class AbstractFilterableTest {
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 @@ class AbstractFilterableTest {
// 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 @@ class AbstractFilterableTest {
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 @@ class AbstractFilterableTest {
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 671d998258..0f5cc2231e 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,8 +19,11 @@ package org.apache.logging.log4j.core.filter;
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.assertNull;
+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.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.apache.logging.log4j.Level;
@@ -37,19 +40,23 @@ import org.junit.jupiter.api.Test;
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,16 +72,16 @@ class RegexFilterTest {
.setMessage(new SimpleMessage("test")) //
.build();
assertSame(Filter.Result.DENY, filter.filter(event));
- filter = RegexFilter.createFilter(null, null, false, null, null);
- assertNull(filter);
+ final RegexFilter.Builder filterBuilder = RegexFilter.newBuilder();
+ assertThrows(IllegalArgumentException.class, filterBuilder::build);
}
@Test
void testDotAllPattern() throws Exception {
final String singleLine = "test single line matches";
final String multiLine = "test multi line matches\nsome more lines";
- final RegexFilter filter = RegexFilter.createFilter(
- ".*line.*", new String[] {"DOTALL", "COMMENTS"}, false,
Filter.Result.DENY, Filter.Result.ACCEPT);
+ final RegexFilter filter =
+ RegexFilter.createFilter("(?xs).*line.*", null, false,
Filter.Result.DENY, Filter.Result.ACCEPT);
final Result singleLineResult = filter.filter(null, null, null,
(Object) singleLine, null);
final Result multiLineResult = filter.filter(null, null, null,
(Object) multiLine, null);
assertThat(singleLineResult, equalTo(Result.DENY));
@@ -82,9 +89,17 @@ class RegexFilterTest {
}
@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,100 @@ class RegexFilterTest {
}
@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();
+ assertThrows(IllegalArgumentException.class, 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");
+ assertThrows(IllegalArgumentException.class, 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 397390bcbc..13ae124072 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 class AbstractFilter extends
AbstractLifeCycle implements Filter
public static final String ATTR_ON_MISMATCH = "onMismatch";
public static final String ATTR_ON_MATCH = "onMatch";
+ /**
+ * The action to perform when a match occurs.
+ */
@PluginBuilderAttribute(ATTR_ON_MATCH)
private Result onMatch = Result.NEUTRAL;
+ /**
+ * The action to perform when a mismatch occurs.
+ */
@PluginBuilderAttribute(ATTR_ON_MISMATCH)
private Result onMismatch = Result.DENY;
+ /**
+ * Returns the action to apply when a match occurs
+ * @return the match result
+ */
public Result getOnMatch() {
return onMatch;
}
+ /**
+ * Returns the action to apply when a mismatch occurs
+ * @return the mismatch result
+ */
public Result getOnMismatch() {
return onMismatch;
}
@@ -110,6 +126,18 @@ public abstract class AbstractFilter extends
AbstractLifeCycle implements Filter
this.onMismatch = onMismatch == null ? Result.DENY : onMismatch;
}
+ /**
+ * Constructs a new instance configured by the given builder
+ * @param builder the builder
+ * @throws NullPointerException if the builder argument is {@code null}
+ * @since 2.27.0
+ */
+ protected AbstractFilter(final AbstractFilterBuilder<?> builder) {
+ Objects.requireNonNull(builder, "builder");
+ 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 62d41b31f5..67dbbaf870 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
@@ -16,10 +16,7 @@
*/
package org.apache.logging.log4j.core.filter;
-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 +26,212 @@ 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.PluginElement;
-import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+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.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}
+ * @since 2.27.0
+ */
+ public Pattern getPattern() {
+ return this.pattern;
+ }
+
+ /**
+ * Returns the regular-expression.
+ * @return the regular-expression (it may be an empty string but never
{@code null})
+ * @since 2.27.0
+ */
+ 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}
+ * @since 2.27.0
+ */
+ public boolean isUseRawMessage() {
+ return this.useRawMessage;
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This implementation performs the filter evaluation against the given
message formatted with
+ * the given parameters.
+ * </p>
+ * <p>
+ * The following method arguments are ignored by this filter method
implementation:
+ * <ul>
+ * <li>{@code logger}</li>
+ * <li>{@code level}</li>
+ * <li>{@code marker}</li>
+ * </ul>
+ * </p>
+ */
@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}
+ * <p>
+ * This implementation performs the filter evaluation against the given
message.
+ * </p>
+ * <p>
+ * The following method arguments are ignored by this filter method
implementation:
+ * <ul>
+ * <li>{@code logger}</li>
+ * <li>{@code level}</li>
+ * <li>{@code marker}</li>
+ * <li>{@code throwable}</li>
+ * </ul>
+ * </p>
+ */
@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}
+ * <p>
+ * This implementation performs the filter evaluation against the given
message.
+ * </p>
+ * <p>
+ * The following method arguments are ignored by this filter method
implementation:
+ * <ul>
+ * <li>{@code logger}</li>
+ * <li>{@code level}</li>
+ * <li>{@code marker}</li>
+ * <li>{@code throwable}</li>
+ * </ul>
+ * </p>
+ */
@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, "event");
+ return filter(getMessageTextByType(event.getMessage()));
}
- // While `Message#getFormat()` is broken in general, it still makes sense
for certain types.
- // Hence, suppress the deprecation warning.
+ /**
+ * Apply the filter to the given message and return the {@code onMatch}
result if the <i>entire</i>
+ * message matches the configured regex pattern; otherwise, {@code
onMismatch}.
+ * <p>
+ * If the given '{@code msg}' is {@code null} the configured {@code
onMismatch} result will be returned.
+ * </p>
+ * @param msg the message
+ * @return the {@code onMatch} result if the pattern matches; otherwise,
the {@code onMismatch} result
+ */
+ Result filter(final @Nullable String msg) {
+ return (msg != null && pattern.matcher(msg).matches()) ? onMatch :
onMismatch;
+ }
+
+ /**
+ * Tests the filter pattern against the given Log4j {@code Message}.
+ * <p>
+ * If the raw-message flag is enabled and message is an instance of the
following, the raw message format
+ * will be returned.
+ * </p>
+ * <ul>
+ * <li>{@link MessageFormatMessage}</li>
+ * <li>{@link ParameterizedMessage}</li>
+ * <li>{@link StringFormattedMessage}</li>
+ * <li>{@link StructuredDataMessage}</li>
+ * </ul>
+ * <p>
+ * If the '{@code useRawMessage}' flag is disabled <i>OR</i> the message
is not one of the above
+ * implementations, the message's formatted message will be returned.
+ * </p>
+ * <h3>Developer Note</h3>
+ * <p>
+ * While `Message#getFormat()` is broken in general, it still makes sense
for certain types.
+ * Hence, suppress the deprecation warning.
+ * </p>
+ *
+ * @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,78 +241,146 @@ public final class RegexFilter extends AbstractFilter {
: message.getFormattedMessage();
}
- private Result filter(final String msg) {
- if (msg == null) {
- return onMismatch;
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return "useRawMessage=" + useRawMessage + ", pattern=" + pattern;
+ }
+
+ /**
+ * Creates a new builder instance.
+ * @return the new builder instance
+ * @since 2.27.0
+ */
+ @PluginBuilderFactory
+ public static Builder newBuilder() {
+ return new Builder();
+ }
+
+ /**
+ * A {@link RegexFilter} builder instance.
+ * @since 2.27.0
+ */
+ public static final class Builder extends
AbstractFilterBuilder<RegexFilter.Builder>
+ implements org.apache.logging.log4j.core.util.Builder<RegexFilter>
{
+
+ /**
+ * 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 RegexFilter build() {
+
+ // validate the "regex" attribute
+ if (Strings.isEmpty(this.regex)) {
+ throw new IllegalArgumentException(
+ "Unable to create `RegexFilter`: The `regex` attribute
be set to a non-empty string.");
+ }
+
+ // build with *safety* to not throw exceptions
+ try {
+ return new RegexFilter(this);
+ } catch (final Exception ex) {
+ throw new IllegalArgumentException("Unable to create
`RegexFilter`.", ex);
+ }
}
- 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();
+ /**
+ * @deprecated use {@link RegexFilter.Builder} instead
+ */
+ @SuppressWarnings("unused")
+ @Deprecated
+ private RegexFilter(
+ final String regex,
+ final boolean useRawMessage,
+ // `patternFlags` has never worked, and removed in
`2.27.0`.
+ // We're keeping this field for binary backward
compatibility.
+ final @Nullable String @Nullable [] patternFlags,
+ final @Nullable Result onMatch,
+ final @Nullable Result onMismatch) {
+ super(onMatch, onMismatch);
+ Objects.requireNonNull(regex, "regex");
+ this.pattern = Pattern.compile(regex);
+ this.useRawMessage = useRawMessage;
}
/**
* 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 Ignored, kept for backward compatibility.
+ * @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)
- // @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);
- }
-
- private static int toPatternFlags(final String[] patternFlags)
+ // `patternFlags` has never worked, and removed in `2.27.0`.
+ // We're keeping this field for binary backward compatibility.
+ final String @Nullable [] patternFlags,
+ @PluginAttribute("useRawMsg") final @Nullable Boolean useRawMsg,
+ @PluginAttribute("onMatch") final @Nullable Result match,
+ @PluginAttribute("onMismatch") final @Nullable Result mismatch)
throws IllegalArgumentException, IllegalAccessException {
- if (patternFlags == null || patternFlags.length == 0) {
- return DEFAULT_PATTERN_FLAGS;
- }
- final Field[] fields = Pattern.class.getDeclaredFields();
- final Comparator<Field> comparator = (f1, f2) ->
f1.getName().compareTo(f2.getName());
- Arrays.sort(fields, comparator);
- final String[] fieldNames = new String[fields.length];
- for (int i = 0; i < fields.length; i++) {
- fieldNames[i] = fields[i].getName();
- }
- int flags = DEFAULT_PATTERN_FLAGS;
- for (final String test : patternFlags) {
- final int index = Arrays.binarySearch(fieldNames, test);
- if (index >= 0) {
- final Field field = fields[index];
- flags |= field.getInt(Pattern.class);
- }
- }
- return flags;
+ Objects.requireNonNull(regex, "regex");
+ return new RegexFilter(regex, Boolean.TRUE.equals(useRawMsg),
patternFlags, match, mismatch);
}
}
diff --git
a/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/package-info.java
b/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/package-info.java
index 38173ef473..6794a54f74 100644
---
a/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/package-info.java
+++
b/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/package-info.java
@@ -22,7 +22,7 @@
* {@link org.apache.logging.log4j.core.Filter#ELEMENT_TYPE filter}.
*/
@Export
-@Version("2.25.3")
+@Version("2.27.0")
package org.apache.logging.log4j.core.filter;
import org.osgi.annotation.bundle.Export;
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 10bc1a9f52..8a772dc733 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> {
*/
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/regexfilter_patternflags_builder.xml
b/src/changelog/.2.x.x/regexfilter_patternflags_builder.xml
new file mode 100644
index 0000000000..9ba2fc8982
--- /dev/null
+++ b/src/changelog/.2.x.x/regexfilter_patternflags_builder.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<entry xmlns="https://logging.apache.org/xml/ns"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="https://logging.apache.org/xml/ns
https://logging.apache.org/xml/ns/log4j-changelog-0.xsd"
+ type="removed">
+ <issue id="3239"
link="https://github.com/apache/logging-log4j2/issues/3239"/>
+ <issue id="3512" link="https://github.com/apache/logging-log4j2/pull/3512"/>
+ <issue id="4119" link="https://github.com/apache/logging-log4j2/pull/4119"/>
+ <description format="asciidoc">
+ Remove the `patternFlags` attribute from `RegexFilter`.
+ It was not documented and it has never worked
+ Note that pattern flags can be provided as a part of the pattern (e.g.,
`(?s)`, `(?m)`) in the `regex` attribute.
+ </description>
+</entry>
+