From e8bbd1d78275103b2c2bdaf9c1657530dd77d447 Mon Sep 17 00:00:00 2001
From: Jeff Thomas
Date: Sun, 2 Mar 2025 21:45:23 +0100
Subject: [PATCH 01/12] Removed 'patternFlags' @PluginAttribute from
RegexFilter @PluginFactory createFilter. (#3086) 8d05a7
---
.../log4j/core/filter/RegexFilter.java | 91 +++++++++++--------
...remove_patternflags_from_PluginFactory.xml | 10 ++
2 files changed, 63 insertions(+), 38 deletions(-)
create mode 100644 src/changelog/2.25.0/3086_change_RegexFilter_remove_patternflags_from_PluginFactory.xml
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 400bd42e01e..52db17e8c3a 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,9 +16,6 @@
*/
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.regex.Pattern;
import org.apache.logging.log4j.Level;
@@ -26,6 +23,10 @@
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.message.Message;
import org.apache.logging.log4j.message.ParameterizedMessage;
import org.apache.logging.log4j.plugins.Configurable;
@@ -45,7 +46,6 @@
@Plugin
public final class RegexFilter extends AbstractFilter {
- private static final int DEFAULT_PATTERN_FLAGS = 0;
private final Pattern pattern;
private final boolean useRawMessage;
@@ -101,10 +101,7 @@ private Result filter(final String msg) {
@Override
public String toString() {
- final StringBuilder sb = new StringBuilder();
- sb.append("useRaw=").append(useRawMessage);
- sb.append(", pattern=").append(pattern.toString());
- return sb.toString();
+ return "useRaw=" + useRawMessage + ", pattern=" + pattern.toString();
}
/**
@@ -114,6 +111,7 @@ public String toString() {
* 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 true, the raw message will be used, otherwise the formatted message will be used.
* @param onMatch
@@ -121,47 +119,64 @@ public String toString() {
* @param onMismatch
* The action to perform when a mismatch occurs.
* @return The RegexFilter.
- * @throws IllegalAccessException
- * @throws IllegalArgumentException
+ * @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 #createFilter(String, Boolean, Result, Result)}
+ */
+ @Deprecated
+ // TODO Consider refactoring to use AbstractFilter.AbstractFilterBuilder
+ public static RegexFilter createFilter(
+ // @formatter:off
+ @PluginAttribute("regex") final String regex,
+ final String[] patternFlags,
+ @PluginAttribute("useRawMsg") final Boolean useRawMsg,
+ @PluginAttribute("onMatch") final Result match,
+ @PluginAttribute("onMismatch") final Result mismatch)
+ // @formatter:on
+ throws IllegalArgumentException, IllegalAccessException {
+
+ // LOG4J-3086 - pattern-flags can be embedded in RegEx expression
+
+ return createFilter(regex, useRawMsg, match, mismatch);
+ }
+
+ /**
+ * Creates a Filter that matches a regular expression.
+ *
+ * @param regex
+ * The regular expression to match.
+ * @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 IllegalArgumentException When passed an illegal or inappropriate argument.
*/
// TODO Consider refactoring to use AbstractFilter.AbstractFilterBuilder
@PluginFactory
public static RegexFilter createFilter(
// @formatter:off
- @PluginAttribute final String regex,
- @PluginElement final String[] patternFlags,
- @PluginAttribute final Boolean useRawMsg,
- @PluginAttribute final Result onMatch,
- @PluginAttribute final Result onMismatch)
+ @PluginAttribute("regex") final String regex,
+ @PluginAttribute("useRawMsg") final Boolean useRawMsg,
+ @PluginAttribute("onMatch") final Result match,
+ @PluginAttribute("onMismatch") final Result mismatch)
// @formatter:on
throws IllegalArgumentException, IllegalAccessException {
+ boolean raw = Boolean.TRUE.equals(useRawMsg);
if (regex == null) {
LOGGER.error("A regular expression must be provided for RegexFilter");
return null;
}
- return new RegexFilter(useRawMsg, Pattern.compile(regex, toPatternFlags(patternFlags)), onMatch, onMismatch);
- }
-
- private static int toPatternFlags(final String[] patternFlags)
- throws IllegalArgumentException, IllegalAccessException {
- if (patternFlags == null || patternFlags.length == 0) {
- return DEFAULT_PATTERN_FLAGS;
- }
- final Field[] fields = Pattern.class.getDeclaredFields();
- final Comparator 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);
- }
+ final Pattern pattern;
+ try {
+ pattern = Pattern.compile(regex);
+ } catch (final Exception ex) {
+ LOGGER.error("Unable to compile regular expression: {}", regex, ex);
+ return null;
}
- return flags;
+ return new RegexFilter(raw, pattern, match, mismatch);
}
}
diff --git a/src/changelog/2.25.0/3086_change_RegexFilter_remove_patternflags_from_PluginFactory.xml b/src/changelog/2.25.0/3086_change_RegexFilter_remove_patternflags_from_PluginFactory.xml
new file mode 100644
index 00000000000..0e61653f85c
--- /dev/null
+++ b/src/changelog/2.25.0/3086_change_RegexFilter_remove_patternflags_from_PluginFactory.xml
@@ -0,0 +1,10 @@
+
+
+
+
+ Removed 'patternFlags' @PluginAttribute from RegexFilter @PluginFactory createFilter.
+
+
From cdd9eed13f4add77a2fc063d65e3b7ba1330ae5d Mon Sep 17 00:00:00 2001
From: Jeff Thomas
Date: Sun, 2 Mar 2025 21:48:05 +0100
Subject: [PATCH 02/12] Updates per PR Code Review (#3086)
+ made AbstractFiltter.AbstractFilterBuilder onMatch/onMismatch fields protected
+ added AbstractFilter(AbstractFilterBuilder) constructor
+ added RegexFilter.Builder implementation
+ added RegexFilter(Builder) constructor
+ moved RegexFilter Pattern compile into constructor
+ added fields to persist configuration propertties + getters (regexExpression, patternFlags)
+ changed private constructor to accept builder as argument
+ renamed private method 'targetMessageTest' to more approprriate 'getMessageTextByType'
+ added Javadoc
+ grouped deprecations
---
.../log4j/core/filter/RegexFilterTest.java | 19 +-
.../log4j/core/filter/AbstractFilter.java | 33 +-
.../log4j/core/filter/RegexFilter.java | 355 ++++++++++++++----
.../logging/log4j/core/util/Builder.java | 9 +-
...remove_patternflags_from_PluginFactory.xml | 0
5 files changed, 342 insertions(+), 74 deletions(-)
rename src/changelog/{2.25.0 => .3.x.x}/3086_change_RegexFilter_remove_patternflags_from_PluginFactory.xml (100%)
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 a3e8bf3d025..61ecdf83f31 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
@@ -18,6 +18,8 @@
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.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;
@@ -40,8 +42,19 @@ public static void before() {
}
@Test
- public void testThresholds() throws Exception {
- RegexFilter filter = RegexFilter.createFilter(".* test .*", null, false, null, null);
+ void testRegexFilterDoesNotThrowWithAllTheParametersExceptRegexEqualNull() {
+ assertDoesNotThrow(() -> {
+ RegexFilter.newBuilder().setRegex(".* test .*").build();
+ });
+ }
+
+ @Test
+ void testThresholds() throws Exception {
+ RegexFilter filter = RegexFilter.newBuilder()
+ .setRegex(".* test .*")
+ .setUseRawMsg(false)
+ .build();
+ assertNotNull(filter);
filter.start();
assertTrue(filter.isStarted());
assertSame(
@@ -59,7 +72,7 @@ public 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);
}
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 d9b3d1dbf78..4c76b65b018 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 52db17e8c3a..cae5ba80b54 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,7 +16,10 @@
*/
package org.apache.logging.log4j.core.filter;
-import java.util.regex.Matcher;
+import java.lang.reflect.Field;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Objects;
import java.util.regex.Pattern;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Marker;
@@ -26,7 +29,9 @@
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.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.message.Message;
import org.apache.logging.log4j.message.ParameterizedMessage;
import org.apache.logging.log4j.plugins.Configurable;
@@ -46,137 +51,355 @@
@Plugin
public final class RegexFilter extends AbstractFilter {
+ /** The regular-expression. */
+ private final String regex;
+
+ /** 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 cannot be compiled to a pattern
+ */
+ private RegexFilter(final Builder builder) {
+
+ super(builder);
+
+ this.regex = builder.regex;
+ this.useRawMessage = Boolean.TRUE.equals(builder.useRawMsg);
+
+ try {
+ this.pattern = Pattern.compile(regex);
+ } catch (final Exception ex) {
+ throw new IllegalArgumentException("Unable to compile regular expression: '" + regex + "'.", ex);
+ }
+ }
+
+ /**
+ * Returns the regular-expression.
+ * @return the regular-expression (it may be an empty string but never {@code null})
+ */
+ public String getRegex() {
+ return this.regex;
}
+ /**
+ * Returns the compiled regular-expression pattern.
+ * @return the pattern (will never be {@code null}
+ */
+ public Pattern getPattern() {
+ return this.pattern;
+ }
+
+ /**
+ * Returns whether the raw-message should be used.
+ * @return {@code} if the raw message should be used; otherwise, {@code false}
+ */
+ public boolean isUseRawMessage() {
+ return this.useRawMessage;
+ }
+
+ /** {@inheritDoc} */
@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));
+ return (useRawMessage || params == null || params.length == 0)
+ ? filter(msg)
+ : filter(ParameterizedMessage.format(msg, params));
}
+ /** {@inheritDoc} */
@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());
+ return (msg == null) ? this.onMismatch : filter(msg.toString());
}
+ /** {@inheritDoc} */
@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 = useRawMessage ? msg.getFormat() : msg.getFormattedMessage();
- return filter(text);
+ return filter(getMessageTextByType(msg));
}
+ /** {@inheritDoc} */
@Override
public Result filter(final LogEvent event) {
- final String text = useRawMessage
- ? event.getMessage().getFormat()
- : event.getMessage().getFormattedMessage();
- return filter(text);
+ return filter(getMessageTextByType(event.getMessage()));
}
+ /**
+ * Apply the filter to the given message and return the match/mismatch result.
+ *
+ * If the given '{@code msg}' is {@code null} the configured mismatch result will be returned.
+ *
+ * @param msg the message
+ * @return the filter result
+ */
private Result filter(final String msg) {
if (msg == null) {
return onMismatch;
}
- final Matcher m = pattern.matcher(msg);
- return m.matches() ? onMatch : onMismatch;
+ return pattern.matcher(msg).matches() ? onMatch : onMismatch;
+ }
+
+ /**
+ * 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.
+ *
+ *
+ * - {@link MessageFormatMessage}
+ * - {@link ParameterizedMessage}
+ * - {@link StringFormattedMessage}
+ * - {@link StructuredDataMessage}
+ *
+ *
+ * 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 getMessageTextByType(final Message message) {
+ return useRawMessage
+ && (message instanceof ParameterizedMessage
+ || message instanceof StringFormattedMessage
+ || message instanceof MessageFormatMessage
+ || message instanceof StructuredDataMessage)
+ ? message.getFormat()
+ : message.getFormattedMessage();
}
@Override
public String toString() {
- return "useRaw=" + useRawMessage + ", pattern=" + pattern.toString();
+ return "useRawMessage=" + useRawMessage + ", regex=" + regex + ", pattern=" + pattern.toString();
}
/**
- * Creates a Filter that matches a regular expression.
+ * Creates a new builder instance.
*
- * @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 true, the raw message will be used, otherwise the formatted message will be used.
- * @param onMatch
- * The action to perform when a match occurs.
- * @param onMismatch
- * 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 IllegalArgumentException When passed an illegal or inappropriate argument.
- * @deprecated use {@link #createFilter(String, Boolean, Result, Result)}
+ * @return the new builder instance
+ */
+ @PluginBuilderFactory
+ public static RegexFilter.Builder newBuilder() {
+ return new RegexFilter.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
+ private 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 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 = regex;
+ 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 (regex != null);
+ }
+
+ /**
+ * 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() {
+
+ if (!isValid()) {
+ return null;
+ }
+
+ try {
+ return new RegexFilter(this);
+ } catch (final Exception ex) {
+ LOGGER.error("Unable to create RegexFilter. {}", ex.getMessage(), ex);
+ return null;
+ }
+ }
+ }
+
+ /*
+ * 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
- // TODO Consider refactoring to use AbstractFilter.AbstractFilterBuilder
- public static RegexFilter createFilter(
- // @formatter:off
- @PluginAttribute("regex") final String regex,
- final String[] patternFlags,
- @PluginAttribute("useRawMsg") final Boolean useRawMsg,
- @PluginAttribute("onMatch") final Result match,
- @PluginAttribute("onMismatch") final Result mismatch)
- // @formatter:on
- throws IllegalArgumentException, IllegalAccessException {
+ private static final int DEFAULT_PATTERN_FLAGS = 0;
- // LOG4J-3086 - pattern-flags can be embedded in RegEx expression
+ /**
+ * @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 boolean useRawMessage,
+ final String regex,
+ final String[] patternFlags,
+ final Result onMatch,
+ final Result onMismatch) {
+ super(onMatch, onMismatch);
+ this.regex = 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;
+ }
- return createFilter(regex, useRawMsg, match, mismatch);
+ /**
+ * 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 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)
// @formatter:on
throws IllegalArgumentException, IllegalAccessException {
+
+ // LOG4J-3086 - pattern-flags can be embedded in RegEx expression
+
boolean raw = Boolean.TRUE.equals(useRawMsg);
if (regex == null) {
LOGGER.error("A regular expression must be provided for RegexFilter");
return null;
}
- final Pattern pattern;
+
try {
- pattern = Pattern.compile(regex);
+ return new RegexFilter(raw, regex, patternFlags, match, mismatch);
} catch (final Exception ex) {
- LOGGER.error("Unable to compile regular expression: {}", regex, ex);
+ LOGGER.error("Unable to create RegexFilter. {}", ex.getMessage(), ex);
return null;
}
- return new RegexFilter(raw, pattern, match, mismatch);
+ }
+
+ /** @deprecated pattern flags have been deprecated - they can just be included in the regex-expression. */
+ @Deprecated
+ private static int toPatternFlags(final String[] patternFlags)
+ throws IllegalArgumentException, IllegalAccessException {
+ if (patternFlags == null || patternFlags.length == 0) {
+ return DEFAULT_PATTERN_FLAGS;
+ }
+ final Field[] fields = Pattern.class.getDeclaredFields();
+ final Comparator 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;
}
}
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 e99250edcc2..7d3f9bf65eb 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
@@ -45,8 +45,11 @@ public interface Builder extends Supplier {
*/
T build();
- @Override
- default T get() {
- return 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.25.0/3086_change_RegexFilter_remove_patternflags_from_PluginFactory.xml b/src/changelog/.3.x.x/3086_change_RegexFilter_remove_patternflags_from_PluginFactory.xml
similarity index 100%
rename from src/changelog/2.25.0/3086_change_RegexFilter_remove_patternflags_from_PluginFactory.xml
rename to src/changelog/.3.x.x/3086_change_RegexFilter_remove_patternflags_from_PluginFactory.xml
From 3827a0c6d47eaf6e5a47ffa863828813a25d05ad Mon Sep 17 00:00:00 2001
From: Jeff Thomas
Date: Sun, 2 Mar 2025 21:48:39 +0100
Subject: [PATCH 03/12] A few more improvements + added tests (#3086)
---
.../log4j/core/filter/RegexFilterTest.java | 126 +++++++++++++++---
.../log4j/core/filter/RegexFilter.java | 42 ++++--
2 files changed, 142 insertions(+), 26 deletions(-)
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 61ecdf83f31..c5a1373332e 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,9 +19,12 @@
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.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.apache.logging.log4j.Level;
@@ -89,9 +92,18 @@ public void testDotAllPattern() throws Exception {
}
@Test
- public 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, (Throwable) null));
assertSame(Filter.Result.DENY, filter.filter(null, Level.DEBUG, null, (Message) null, (Throwable) null));
@@ -99,28 +111,112 @@ public void testNoMsg() throws Exception {
}
@Test
- public 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);
+
+ assertTrue(builder.isValid());
+
+ 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();
+
+ assertFalse(builder.isValid());
+
+ 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");
+
+ assertFalse(builder.isValid());
+
+ assertNull(builder.build());
+
+ }
}
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 cae5ba80b54..fc9ea1a4511 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
@@ -21,6 +21,7 @@
import java.util.Comparator;
import java.util.Objects;
import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.core.Filter;
@@ -51,9 +52,6 @@
@Plugin
public final class RegexFilter extends AbstractFilter {
- /** The regular-expression. */
- private final String regex;
-
/** The pattern compiled from the regular-expression. */
private final Pattern pattern;
@@ -69,13 +67,12 @@ private RegexFilter(final Builder builder) {
super(builder);
- this.regex = builder.regex;
this.useRawMessage = Boolean.TRUE.equals(builder.useRawMsg);
try {
- this.pattern = Pattern.compile(regex);
+ this.pattern = Pattern.compile(builder.regex);
} catch (final Exception ex) {
- throw new IllegalArgumentException("Unable to compile regular expression: '" + regex + "'.", ex);
+ throw new IllegalArgumentException("Unable to compile regular expression: '" + builder.regex + "'.", ex);
}
}
@@ -84,7 +81,7 @@ private RegexFilter(final Builder builder) {
* @return the regular-expression (it may be an empty string but never {@code null})
*/
public String getRegex() {
- return this.regex;
+ return this.pattern.pattern();
}
/**
@@ -143,7 +140,7 @@ public Result filter(final LogEvent event) {
* @param msg the message
* @return the filter result
*/
- private Result filter(final String msg) {
+ public Result filter(final String msg) {
if (msg == null) {
return onMismatch;
}
@@ -188,7 +185,7 @@ private String getMessageTextByType(final Message message) {
@Override
public String toString() {
- return "useRawMessage=" + useRawMessage + ", regex=" + regex + ", pattern=" + pattern.toString();
+ return "useRawMessage=" + useRawMessage + ", pattern=" + pattern.toString();
}
/**
@@ -258,7 +255,11 @@ public Builder setUseRawMsg(final boolean useRawMsg) {
*/
@Override
public boolean isValid() {
- return (regex != null);
+ boolean valid = true;
+ if (!isRegexValid()) {
+ valid = false;
+ }
+ return valid;
}
/**
@@ -280,6 +281,25 @@ public RegexFilter build() {
return null;
}
}
+
+ /**
+ * Validates the 'regex' attribute.
+ *
+ * If the regular-expression is not set, or cannot be compiled to a valid pattern the validation will fail.
+ *
+ * @return {@code true} if the regular-expression is valid; otherwise, {@code false}
+ */
+ private boolean isRegexValid() {
+ if (regex == null) {
+ return false;
+ }
+ try {
+ Pattern.compile(regex);
+ } catch (final PatternSyntaxException ex) {
+ return false;
+ }
+ return true;
+ }
}
/*
@@ -313,7 +333,7 @@ private RegexFilter(
final Result onMatch,
final Result onMismatch) {
super(onMatch, onMismatch);
- this.regex = Objects.requireNonNull(regex, "The 'regex' argument must be provided for RegexFilter");
+ 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);
From 4d60384c2752c955ff64d916312556b90687080d Mon Sep 17 00:00:00 2001
From: Jeff Thomas
Date: Sun, 2 Mar 2025 21:49:50 +0100
Subject: [PATCH 04/12] Fix validation behavior in RegexFilter (#3086)
+ added validation checks to RegexFilter
+ added JVerify nullability annotations to RegexFilter
+ updated javadoc
+ replaced deprecated usages of CompositeFilter#getFilters with CompositeFilter#getFiltersArray in AbstractFilterableTest
---
.../log4j/core/filter/AbstractFilterTest.java | 1 -
.../core/filter/AbstractFilterableTest.java | 20 +-
.../log4j/core/filter/RegexFilterTest.java | 57 ++---
.../log4j/core/filter/RegexFilter.java | 231 ++++++++++--------
4 files changed, 160 insertions(+), 149 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 34f0f7e6967..9bb1aabaecd 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 @@ public class AbstractFilterTest {
@Test
public 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 2bd3e95deb0..10a3327de0f 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
@@ -53,7 +53,7 @@ public void testAddMultipleSimpleFilters() throws Exception {
// adding a second filter converts the filter
// into a CompositeFilter.class
filterable.addFilter(filter);
- assertTrue(filterable.getFilter() instanceof CompositeFilter);
+ assertInstanceOf(CompositeFilter.class, filterable.getFilter());
assertEquals(2, ((CompositeFilter) filterable.getFilter()).getFiltersArray().length);
}
@@ -66,7 +66,7 @@ public void testAddMultipleEqualSimpleFilter() throws Exception {
// adding a second filter converts the filter
// into a CompositeFilter.class
filterable.addFilter(filter);
- assertTrue(filterable.getFilter() instanceof CompositeFilter);
+ assertInstanceOf(CompositeFilter.class, filterable.getFilter());
assertEquals(2, ((CompositeFilter) filterable.getFilter()).getFiltersArray().length);
}
@@ -92,7 +92,7 @@ public void testAddMultipleCompositeFilters() throws Exception {
// adding a second filter converts the filter
// into a CompositeFilter.class
filterable.addFilter(compositeFilter);
- assertTrue(filterable.getFilter() instanceof CompositeFilter);
+ assertInstanceOf(CompositeFilter.class, filterable.getFilter());
assertEquals(6, ((CompositeFilter) filterable.getFilter()).getFiltersArray().length);
}
@@ -108,7 +108,7 @@ public void testAddSimpleFilterAndCompositeFilter() throws Exception {
// adding a second filter converts the filter
// into a CompositeFilter.class
filterable.addFilter(compositeFilter);
- assertTrue(filterable.getFilter() instanceof CompositeFilter);
+ assertInstanceOf(CompositeFilter.class, filterable.getFilter());
assertEquals(2, ((CompositeFilter) filterable.getFilter()).getFiltersArray().length);
}
@@ -124,7 +124,7 @@ public void testAddCompositeFilterAndSimpleFilter() throws Exception {
// adding a second filter converts the filter
// into a CompositeFilter.class
filterable.addFilter(notInCompositeFilterFilter);
- assertTrue(filterable.getFilter() instanceof CompositeFilter);
+ assertInstanceOf(CompositeFilter.class, filterable.getFilter());
assertEquals(3, ((CompositeFilter) filterable.getFilter()).getFiltersArray().length);
}
@@ -169,7 +169,7 @@ public void testRemoveSimpleEqualFilterFromMultipleSimpleFilters() throws Except
filterable.addFilter(filterOriginal);
filterable.addFilter(filterCopy);
filterable.removeFilter(filterCopy);
- assertTrue(filterable.getFilter() instanceof CompositeFilter);
+ assertInstanceOf(CompositeFilter.class, filterable.getFilter());
assertEquals(2, ((CompositeFilter) filterable.getFilter()).getFiltersArray().length);
filterable.removeFilter(filterCopy);
assertEquals(filterOriginal, filterable.getFilter());
@@ -223,7 +223,7 @@ public void testRemoveSimpleFilterFromCompositeAndSimpleFilter() {
// should not remove internal filter of compositeFilter
filterable.removeFilter(anotherFilter);
- assertTrue(filterable.getFilter() instanceof CompositeFilter);
+ assertInstanceOf(CompositeFilter.class, filterable.getFilter());
assertEquals(2, ((CompositeFilter) filterable.getFilter()).getFiltersArray().length);
}
@@ -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 c5a1373332e..23c1796f13d 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
@@ -24,7 +24,6 @@
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.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.apache.logging.log4j.Level;
@@ -94,11 +93,10 @@ public void testDotAllPattern() throws Exception {
@Test
void testNoMsg() {
- final RegexFilter filter =
- RegexFilter.newBuilder()
- .setRegex(".* test .*")
- .setUseRawMsg(false)
- .build();
+ final RegexFilter filter = RegexFilter.newBuilder()
+ .setRegex(".* test .*")
+ .setUseRawMsg(false)
+ .build();
assertNotNull(filter);
@@ -116,13 +114,12 @@ void testParameterizedMsg() {
final Object[] params = {"foo", "bar"};
// match against raw message
- final RegexFilter rawFilter =
- RegexFilter.newBuilder()
- .setRegex("params \\{\\} \\{\\}")
- .setUseRawMsg(true)
- .setOnMatch(Result.ACCEPT)
- .setOnMismatch(Result.DENY)
- .build();
+ final RegexFilter rawFilter = RegexFilter.newBuilder()
+ .setRegex("params \\{\\} \\{\\}")
+ .setUseRawMsg(true)
+ .setOnMatch(Result.ACCEPT)
+ .setOnMismatch(Result.DENY)
+ .build();
assertNotNull(rawFilter);
@@ -130,12 +127,12 @@ void testParameterizedMsg() {
assertThat(rawResult, equalTo(Result.ACCEPT));
// match against formatted message
- final RegexFilter fmtFilter =
- RegexFilter.newBuilder()
- .setRegex("params foo bar")
- .setUseRawMsg(false)
- .setOnMatch(Result.ACCEPT)
- .setOnMismatch(Result.DENY).build();
+ final RegexFilter fmtFilter = RegexFilter.newBuilder()
+ .setRegex("params foo bar")
+ .setUseRawMsg(false)
+ .setOnMatch(Result.ACCEPT)
+ .setOnMismatch(Result.DENY)
+ .build();
assertNotNull(fmtFilter);
@@ -151,8 +148,11 @@ 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.Builder builder = RegexFilter.newBuilder()
+ .setRegex(regex)
+ .setUseRawMsg(false)
+ .setOnMatch(Result.ACCEPT)
+ .setOnMismatch(Result.DENY);
assertTrue(builder.isValid());
@@ -172,13 +172,12 @@ 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();
+ final RegexFilter filter = RegexFilter.newBuilder()
+ .setRegex(regex)
+ .setUseRawMsg(false)
+ .setOnMatch(Result.ACCEPT)
+ .setOnMismatch(Result.DENY)
+ .build();
assertNotNull(filter);
@@ -201,7 +200,6 @@ void testBuilderWithoutRegexNotValid() {
assertFalse(builder.isValid());
assertNull(builder.build());
-
}
/**
@@ -217,6 +215,5 @@ void testBuilderWithInvalidRegexNotValid() {
assertFalse(builder.isValid());
assertNull(builder.build());
-
}
}
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 fc9ea1a4511..a04ac76324d 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
@@ -21,7 +21,6 @@
import java.util.Comparator;
import java.util.Objects;
import java.util.regex.Pattern;
-import java.util.regex.PatternSyntaxException;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.core.Filter;
@@ -33,23 +32,22 @@
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.validation.constraints.Required;
+import org.apache.logging.log4j.core.util.Assert;
import org.apache.logging.log4j.message.Message;
import org.apache.logging.log4j.message.ParameterizedMessage;
-import org.apache.logging.log4j.plugins.Configurable;
-import org.apache.logging.log4j.plugins.Plugin;
-import org.apache.logging.log4j.plugins.PluginAttribute;
-import org.apache.logging.log4j.plugins.PluginElement;
-import org.apache.logging.log4j.plugins.PluginFactory;
+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;
/**
- * This filter returns the onMatch result if the message matches the regular expression.
- *
- * The "useRawMsg" attribute can be used to indicate whether the regular expression should be applied to the result of
- * calling Message.getMessageFormat (true) or Message.getFormattedMessage() (false). The default is false.
- *
+ * 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.
*/
-@Configurable(elementType = Filter.ELEMENT_TYPE, printObject = true)
-@Plugin
+@Plugin(name = "RegexFilter", category = Node.CATEGORY, elementType = Filter.ELEMENT_TYPE, printObject = true)
+@NullMarked
public final class RegexFilter extends AbstractFilter {
/** The pattern compiled from the regular-expression. */
@@ -61,12 +59,16 @@ public final class RegexFilter extends AbstractFilter {
/**
* Constructs a new {@code RegexFilter} configured by the given builder.
* @param builder the builder
- * @throws IllegalArgumentException if the regular expression cannot be compiled to a pattern
+ * @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.isNotBlank(builder.regex)) {
+ throw new IllegalArgumentException("The 'regex' attribute must not be null or empty.");
+ }
+
this.useRawMessage = Boolean.TRUE.equals(builder.useRawMsg);
try {
@@ -76,14 +78,6 @@ private RegexFilter(final Builder builder) {
}
}
- /**
- * 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 the compiled regular-expression pattern.
* @return the pattern (will never be {@code null}
@@ -92,59 +86,123 @@ 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} if the raw message should be used; otherwise, {@code false}
+ * @return {@code true} if the raw message should be used; otherwise, {@code false}
*/
public boolean isUseRawMessage() {
return this.useRawMessage;
}
- /** {@inheritDoc} */
+ /**
+ * {@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:
+ *
+ * - {@code logger}
+ * - {@code level}
+ * - {@code marker}
+ *
+ *
+ */
@Override
public Result filter(
- final Logger logger, final Level level, final Marker marker, final String msg, final Object... 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} */
+ /**
+ * {@inheritDoc}
+ *
+ * This implementation performs the filter evaluation against the given message.
+ *
+ *
+ * The following method arguments are ignored by this filter method implementation:
+ *
+ * - {@code logger}
+ * - {@code level}
+ * - {@code marker}
+ * - {@code throwable}
+ *
+ *
+ */
@Override
public Result filter(
- final Logger logger, final Level level, final Marker marker, final Object msg, final Throwable t) {
- return (msg == null) ? this.onMismatch : 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} */
+ /**
+ * {@inheritDoc}
+ *
+ * This implementation performs the filter evaluation against the given message.
+ *
+ *
+ * The following method arguments are ignored by this filter method implementation:
+ *
+ * - {@code logger}
+ * - {@code level}
+ * - {@code marker}
+ * - {@code throwable}
+ *
+ *
+ */
@Override
public Result filter(
- final Logger logger, final Level level, final Marker marker, final Message msg, final Throwable t) {
- if (msg == null) {
- return onMismatch;
- }
- return filter(getMessageTextByType(msg));
+ 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} */
+ /**
+ * {@inheritDoc}
+ *
+ * @throws NullPointerException if the {@code event} argument is {@code null}
+ */
@Override
public Result filter(final LogEvent event) {
+ 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 match/mismatch result.
+ * 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 mismatch result will be returned.
+ * If the given '{@code msg}' is {@code null} the configured {@code onMismatch} result will be returned.
*
* @param msg the message
- * @return the filter result
+ * @return the {@code onMatch} result if the pattern matches; otherwise, the {@code onMismatch} result
*/
- public Result filter(final String msg) {
- if (msg == null) {
- return onMismatch;
- }
- return pattern.matcher(msg).matches() ? onMatch : onMismatch;
+ public Result filter(final @Nullable String msg) {
+ return (msg != null && pattern.matcher(msg).matches()) ? onMatch : onMismatch;
}
/**
@@ -183,6 +241,7 @@ private String getMessageTextByType(final Message message) {
: message.getFormattedMessage();
}
+ /** {@inheritDoc} */
@Override
public String toString() {
return "useRawMessage=" + useRawMessage + ", pattern=" + pattern.toString();
@@ -190,12 +249,11 @@ public String toString() {
/**
* Creates a new builder instance.
- *
* @return the new builder instance
*/
@PluginBuilderFactory
- public static RegexFilter.Builder newBuilder() {
- return new RegexFilter.Builder();
+ public static Builder newBuilder() {
+ return new Builder();
}
/**
@@ -210,7 +268,8 @@ public static final class Builder extends AbstractFilterBuilder
- * If the regular-expression is not set, or cannot be compiled to a valid pattern the validation will fail.
- *
- * @return {@code true} if the regular-expression is valid; otherwise, {@code false}
- */
- private boolean isRegexValid() {
- if (regex == null) {
- return false;
- }
- try {
- Pattern.compile(regex);
- } catch (final PatternSyntaxException ex) {
- return false;
- }
- return true;
- }
}
/*
@@ -327,11 +356,11 @@ private boolean isRegexValid() {
@Deprecated
@SuppressWarnings("MagicConstant")
private RegexFilter(
- final boolean useRawMessage,
final String regex,
- final String[] patternFlags,
- final Result onMatch,
- final Result onMismatch) {
+ 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();
@@ -375,32 +404,22 @@ public String[] getPatternFlags() {
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 {
// LOG4J-3086 - pattern-flags can be embedded in RegEx expression
+ Objects.requireNonNull(regex, "The 'regex' argument must not be null.");
- boolean raw = Boolean.TRUE.equals(useRawMsg);
- if (regex == null) {
- LOGGER.error("A regular expression must be provided for RegexFilter");
- return null;
- }
-
- try {
- return new RegexFilter(raw, regex, patternFlags, match, mismatch);
- } catch (final Exception ex) {
- LOGGER.error("Unable to create RegexFilter. {}", ex.getMessage(), ex);
- return null;
- }
+ return new RegexFilter(regex, Boolean.TRUE.equals(useRawMsg), patternFlags, match, mismatch);
}
/** @deprecated pattern flags have been deprecated - they can just be included in the regex-expression. */
@Deprecated
- private static int toPatternFlags(final String[] patternFlags)
+ private static int toPatternFlags(final String @Nullable [] patternFlags)
throws IllegalArgumentException, IllegalAccessException {
if (patternFlags == null || patternFlags.length == 0) {
return DEFAULT_PATTERN_FLAGS;
From aff317e48afab204abf80d286690c54486c6973c Mon Sep 17 00:00:00 2001
From: Jeff Thomas
Date: Sun, 2 Mar 2025 19:49:09 +0100
Subject: [PATCH 05/12] Fixed a reverse logic check in a validation (#3086)
---
.../log4j/core/config/CompositeConfigurationTest.java | 9 ++++-----
.../apache/logging/log4j/core/filter/RegexFilter.java | 6 +++---
2 files changed, 7 insertions(+), 8 deletions(-)
diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/CompositeConfigurationTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/CompositeConfigurationTest.java
index de4d166524f..4b8cc3034e2 100644
--- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/CompositeConfigurationTest.java
+++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/CompositeConfigurationTest.java
@@ -246,11 +246,10 @@ private AppenderRef getAppenderRef(final List appenderRefList, fina
private void runTest(final LoggerContextRule rule, final Statement statement) {
try {
rule.apply(
- statement,
- Description.createTestDescription(
- getClass(),
- Thread.currentThread().getStackTrace()[1].getMethodName()))
- .evaluate();
+ statement,
+ Description.createTestDescription(getClass(),
+ Thread.currentThread().getStackTrace()[1].getMethodName()))
+ .evaluate();
} catch (final Throwable e) {
Throwables.rethrow(e);
}
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 a04ac76324d..0b26608b163 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
@@ -65,7 +65,7 @@ private RegexFilter(final Builder builder) {
super(builder);
- if (Strings.isNotBlank(builder.regex)) {
+ if (Strings.isBlank(builder.regex)) {
throw new IllegalArgumentException("The 'regex' attribute must not be null or empty.");
}
@@ -316,8 +316,8 @@ public Builder setUseRawMsg(final boolean useRawMsg) {
public @Nullable RegexFilter build() {
// validate the "regex" attribute
- if (this.regex == null) {
- LOGGER.error("Unable to create RegexFilter: The 'regex' attribute must be provided.");
+ if (Strings.isEmpty(this.regex)) {
+ LOGGER.error("Unable to create RegexFilter: The 'regex' attribute be set to a non-empty String.");
return null;
}
From 20d541f8e716545bf5647a1432baa48999729207 Mon Sep 17 00:00:00 2001
From: Jeff Thomas
Date: Sun, 2 Mar 2025 19:58:38 +0100
Subject: [PATCH 06/12] Fix Spotless errors (#3086)
---
.../log4j/core/config/CompositeConfigurationTest.java | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/CompositeConfigurationTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/CompositeConfigurationTest.java
index 4b8cc3034e2..de4d166524f 100644
--- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/CompositeConfigurationTest.java
+++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/CompositeConfigurationTest.java
@@ -246,10 +246,11 @@ private AppenderRef getAppenderRef(final List appenderRefList, fina
private void runTest(final LoggerContextRule rule, final Statement statement) {
try {
rule.apply(
- statement,
- Description.createTestDescription(getClass(),
- Thread.currentThread().getStackTrace()[1].getMethodName()))
- .evaluate();
+ statement,
+ Description.createTestDescription(
+ getClass(),
+ Thread.currentThread().getStackTrace()[1].getMethodName()))
+ .evaluate();
} catch (final Throwable e) {
Throwables.rethrow(e);
}
From d289be39a0f4d8a50c32ccbf59be2854a75dbb06 Mon Sep 17 00:00:00 2001
From: Jeff Thomas
Date: Sun, 2 Mar 2025 21:50:37 +0100
Subject: [PATCH 07/12] Fix RegexFilterTest (#3086)
---
.../logging/log4j/core/filter/RegexFilterTest.java | 10 ++--------
.../apache/logging/log4j/core/filter/RegexFilter.java | 6 ++++++
2 files changed, 8 insertions(+), 8 deletions(-)
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 23c1796f13d..c4b45463c49 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
@@ -39,8 +39,8 @@
public class RegexFilterTest {
@BeforeAll
- public static void before() {
- StatusLogger.getLogger().setLevel(Level.OFF);
+ static void before() {
+ StatusLogger.getLogger().getFallbackListener().setLevel(Level.OFF);
}
@Test
@@ -154,8 +154,6 @@ void testWithValidRegex() {
.setOnMatch(Result.ACCEPT)
.setOnMismatch(Result.DENY);
- assertTrue(builder.isValid());
-
final RegexFilter filter = builder.build();
assertNotNull(filter);
@@ -197,8 +195,6 @@ void testBuilderWithoutRegexNotValid() {
final RegexFilter.Builder builder = RegexFilter.newBuilder();
- assertFalse(builder.isValid());
-
assertNull(builder.build());
}
@@ -212,8 +208,6 @@ void testBuilderWithInvalidRegexNotValid() {
builder.setRegex("[a-z");
- assertFalse(builder.isValid());
-
assertNull(builder.build());
}
}
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 0b26608b163..e37af4eba84 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
@@ -307,6 +307,12 @@ public Builder setUseRawMsg(final boolean useRawMsg) {
return this;
}
+ /** {@inheritDoc} */
+ @Override
+ public boolean isValid() {
+ return (Strings.isNotEmpty(this.regex));
+ }
+
/**
* Builds and returns a {@link RegexFilter} instance configured by this builder.
*
From 0d348a67dc49b887db705f44bb5499e053da6bfb Mon Sep 17 00:00:00 2001
From: Jeff Thomas
Date: Sun, 2 Mar 2025 22:49:17 +0100
Subject: [PATCH 08/12] Cleaned up cherry-pick from 2.x - removed deprecated
API (#3086)
---
.../log4j/core/filter/RegexFilterTest.java | 14 +-
.../log4j/core/filter/RegexFilter.java | 153 +++---------------
2 files changed, 24 insertions(+), 143 deletions(-)
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 c4b45463c49..8f46d891ddb 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
@@ -37,7 +37,7 @@
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
-public class RegexFilterTest {
+class RegexFilterTest {
@BeforeAll
static void before() {
StatusLogger.getLogger().getFallbackListener().setLevel(Level.OFF);
@@ -78,18 +78,6 @@ void testThresholds() throws Exception {
assertNull(filter);
}
- @Test
- public 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 Result singleLineResult = filter.filter(null, null, null, (Object) singleLine, (Throwable) null);
- final Result multiLineResult = filter.filter(null, null, null, (Object) multiLine, (Throwable) null);
- assertThat(singleLineResult, equalTo(Result.DENY));
- assertThat(multiLineResult, equalTo(Result.DENY));
- }
-
@Test
void testNoMsg() {
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 e37af4eba84..33e7f9248ee 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,9 +16,6 @@
*/
package org.apache.logging.log4j.core.filter;
-import java.lang.reflect.Field;
-import java.util.Arrays;
-import java.util.Comparator;
import java.util.Objects;
import java.util.regex.Pattern;
import org.apache.logging.log4j.Level;
@@ -26,18 +23,16 @@
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.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.validation.constraints.Required;
-import org.apache.logging.log4j.core.util.Assert;
import org.apache.logging.log4j.message.Message;
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.plugins.Configurable;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.plugins.PluginBuilderAttribute;
+import org.apache.logging.log4j.plugins.PluginFactory;
+import org.apache.logging.log4j.plugins.util.Assert;
+import org.apache.logging.log4j.plugins.validation.constraints.Required;
import org.apache.logging.log4j.util.Strings;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@@ -45,9 +40,15 @@
/**
* 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.
+ *
+ * The "useRawMsg" attribute can be used to indicate whether the regular expression should be applied to
+ * the result of calling Message.getMessageFormat (true) or Message.getFormattedMessage() (false).
+ * The default is {@code false}.
+ *
*/
-@Plugin(name = "RegexFilter", category = Node.CATEGORY, elementType = Filter.ELEMENT_TYPE, printObject = true)
+@Configurable(elementType = Filter.ELEMENT_TYPE, printObject = true)
@NullMarked
+@Plugin
public final class RegexFilter extends AbstractFilter {
/** The pattern compiled from the regular-expression. */
@@ -65,6 +66,10 @@ private RegexFilter(final Builder builder) {
super(builder);
+ // NOTE: the constructor throws exceptions but is only called from Builder#build() where *null*
+ // should be returned for a misconfigured builder. *If* an exception is thrown here
+ // it will be caught and logged in the builder and not propagated by returning *null*.
+
if (Strings.isBlank(builder.regex)) {
throw new IllegalArgumentException("The 'regex' attribute must not be null or empty.");
}
@@ -128,6 +133,7 @@ public Result filter(
return (useRawMessage || params == null || params.length == 0)
? filter(msg)
: filter(ParameterizedMessage.format(msg, params));
+
}
/**
@@ -212,7 +218,6 @@ public Result filter(final @Nullable String msg) {
* will be returned.
*
*
- * - {@link MessageFormatMessage}
* - {@link ParameterizedMessage}
* - {@link StringFormattedMessage}
* - {@link StructuredDataMessage}
@@ -235,7 +240,6 @@ private String getMessageTextByType(final Message message) {
return useRawMessage
&& (message instanceof ParameterizedMessage
|| message instanceof StringFormattedMessage
- || message instanceof MessageFormatMessage
|| message instanceof StructuredDataMessage)
? message.getFormat()
: message.getFormattedMessage();
@@ -244,14 +248,14 @@ private String getMessageTextByType(final Message message) {
/** {@inheritDoc} */
@Override
public String toString() {
- return "useRawMessage=" + useRawMessage + ", pattern=" + pattern.toString();
+ return "useRawMessage=" + useRawMessage + ", pattern=" + pattern;
}
/**
* Creates a new builder instance.
* @return the new builder instance
*/
- @PluginBuilderFactory
+ @PluginFactory
public static Builder newBuilder() {
return new Builder();
}
@@ -260,7 +264,7 @@ public static Builder newBuilder() {
* A {@link RegexFilter} builder instance.
*/
public static final class Builder extends AbstractFilterBuilder
- implements org.apache.logging.log4j.core.util.Builder {
+ implements org.apache.logging.log4j.plugins.util.Builder {
/* NOTE: LOG4J-3086 - No patternFlags in builder - this functionality has been deprecated/removed. */
@@ -272,8 +276,8 @@ public static final class Builder extends AbstractFilterBuilder 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;
- }
}
From 5a0aee1feb84289dc9d7e1f7c4080d1c19dd5744 Mon Sep 17 00:00:00 2001
From: Jeff Thomas
Date: Sun, 2 Mar 2025 23:23:38 +0100
Subject: [PATCH 09/12] Reverted cherry picked change to legacy Builder.
(#3086)
---
.../java/org/apache/logging/log4j/core/util/Builder.java | 9 +++------
1 file changed, 3 insertions(+), 6 deletions(-)
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 7d3f9bf65eb..e99250edcc2 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
@@ -45,11 +45,8 @@ public interface Builder extends Supplier {
*/
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());
+ @Override
+ default T get() {
+ return build();
}
}
From 2c35ef84cdc47d0994d5b0f3a021fb710e660f9e Mon Sep 17 00:00:00 2001
From: Jeff Thomas
Date: Sun, 2 Mar 2025 23:25:05 +0100
Subject: [PATCH 10/12] Removed 'isValid()' from RegexFilter#Builder() -
validation API has not yet been merged to main (#3086)
---
.../org/apache/logging/log4j/core/filter/RegexFilter.java | 7 -------
1 file changed, 7 deletions(-)
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 33e7f9248ee..992ba03a059 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
@@ -133,7 +133,6 @@ public Result filter(
return (useRawMessage || params == null || params.length == 0)
? filter(msg)
: filter(ParameterizedMessage.format(msg, params));
-
}
/**
@@ -311,11 +310,6 @@ public Builder setUseRawMsg(final boolean useRawMsg) {
return this;
}
- /** {@inheritDoc} */
- public boolean isValid() {
- return (Strings.isNotEmpty(this.regex));
- }
-
/**
* Builds and returns a {@link RegexFilter} instance configured by this builder.
*
@@ -339,5 +333,4 @@ public boolean isValid() {
}
}
}
-
}
From 050567dd06d5b9b47f4c61712931862de3e48279 Mon Sep 17 00:00:00 2001
From: Jeff Thomas
Date: Sun, 2 Mar 2025 23:26:20 +0100
Subject: [PATCH 11/12] Cleaned up AbstractFilterableTest to better match
JUnit5 (#3086)
---
.../core/filter/AbstractFilterableTest.java | 38 +++++++++----------
1 file changed, 19 insertions(+), 19 deletions(-)
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 10a3327de0f..a320ae9fa20 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
@@ -18,16 +18,16 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;
-import static org.junit.jupiter.api.Assertions.assertTrue;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.Filter;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-public class AbstractFilterableTest {
+class AbstractFilterableTest {
MockedAbstractFilterable filterable;
@@ -37,7 +37,7 @@ public void setup() {
}
@Test
- public void testAddSimpleFilter() throws Exception {
+ void testAddSimpleFilter() {
final Filter filter = ThresholdFilter.createFilter(Level.ERROR, null, null);
filterable.addFilter(filter);
@@ -45,7 +45,7 @@ public void testAddSimpleFilter() throws Exception {
}
@Test
- public void testAddMultipleSimpleFilters() throws Exception {
+ void testAddMultipleSimpleFilters() {
final Filter filter = ThresholdFilter.createFilter(Level.ERROR, null, null);
filterable.addFilter(filter);
@@ -58,7 +58,7 @@ public void testAddMultipleSimpleFilters() throws Exception {
}
@Test
- public void testAddMultipleEqualSimpleFilter() throws Exception {
+ void testAddMultipleEqualSimpleFilter() {
final Filter filter = new EqualFilter("test");
filterable.addFilter(filter);
@@ -71,7 +71,7 @@ public void testAddMultipleEqualSimpleFilter() throws Exception {
}
@Test
- public void testAddCompositeFilter() throws Exception {
+ void testAddCompositeFilter() {
final Filter filter1 = ThresholdFilter.createFilter(Level.ERROR, null, null);
final Filter filter2 = ThresholdFilter.createFilter(Level.ERROR, null, null);
final Filter compositeFilter = CompositeFilter.createFilters(new Filter[] {filter1, filter2});
@@ -81,7 +81,7 @@ public void testAddCompositeFilter() throws Exception {
}
@Test
- public void testAddMultipleCompositeFilters() throws Exception {
+ void testAddMultipleCompositeFilters() {
final Filter filter1 = ThresholdFilter.createFilter(Level.ERROR, null, null);
final Filter filter2 = ThresholdFilter.createFilter(Level.ERROR, null, null);
final Filter filter3 = ThresholdFilter.createFilter(Level.ERROR, null, null);
@@ -97,7 +97,7 @@ public void testAddMultipleCompositeFilters() throws Exception {
}
@Test
- public void testAddSimpleFilterAndCompositeFilter() throws Exception {
+ void testAddSimpleFilterAndCompositeFilter() {
final Filter filter1 = ThresholdFilter.createFilter(Level.ERROR, null, null);
final Filter filter2 = ThresholdFilter.createFilter(Level.ERROR, null, null);
final Filter notInCompositeFilterFilter = ThresholdFilter.createFilter(Level.ERROR, null, null);
@@ -113,7 +113,7 @@ public void testAddSimpleFilterAndCompositeFilter() throws Exception {
}
@Test
- public void testAddCompositeFilterAndSimpleFilter() throws Exception {
+ void testAddCompositeFilterAndSimpleFilter() {
final Filter filter1 = ThresholdFilter.createFilter(Level.ERROR, null, null);
final Filter filter2 = ThresholdFilter.createFilter(Level.ERROR, null, null);
final Filter notInCompositeFilterFilter = ThresholdFilter.createFilter(Level.ERROR, null, null);
@@ -129,7 +129,7 @@ public void testAddCompositeFilterAndSimpleFilter() throws Exception {
}
@Test
- public void testRemoveSimpleFilterFromSimpleFilter() throws Exception {
+ void testRemoveSimpleFilterFromSimpleFilter() {
final Filter filter = ThresholdFilter.createFilter(Level.ERROR, null, null);
filterable.addFilter(filter);
@@ -138,7 +138,7 @@ public void testRemoveSimpleFilterFromSimpleFilter() throws Exception {
}
@Test
- public void testRemoveSimpleEqualFilterFromSimpleFilter() throws Exception {
+ void testRemoveSimpleEqualFilterFromSimpleFilter() {
final Filter filterOriginal = new EqualFilter("test");
final Filter filterCopy = new EqualFilter("test");
@@ -148,7 +148,7 @@ public void testRemoveSimpleEqualFilterFromSimpleFilter() throws Exception {
}
@Test
- public void testRemoveSimpleEqualFilterFromTwoSimpleFilters() throws Exception {
+ void testRemoveSimpleEqualFilterFromTwoSimpleFilters() {
final Filter filterOriginal = new EqualFilter("test");
final Filter filterCopy = new EqualFilter("test");
@@ -161,7 +161,7 @@ public void testRemoveSimpleEqualFilterFromTwoSimpleFilters() throws Exception {
}
@Test
- public void testRemoveSimpleEqualFilterFromMultipleSimpleFilters() throws Exception {
+ void testRemoveSimpleEqualFilterFromMultipleSimpleFilters() {
final Filter filterOriginal = new EqualFilter("test");
final Filter filterCopy = new EqualFilter("test");
@@ -178,7 +178,7 @@ public void testRemoveSimpleEqualFilterFromMultipleSimpleFilters() throws Except
}
@Test
- public void testRemoveNullFromSingleSimpleFilter() throws Exception {
+ void testRemoveNullFromSingleSimpleFilter() {
final Filter filter = ThresholdFilter.createFilter(Level.ERROR, null, null);
filterable.addFilter(filter);
@@ -187,7 +187,7 @@ public void testRemoveNullFromSingleSimpleFilter() throws Exception {
}
@Test
- public void testRemoveNonExistingFilterFromSingleSimpleFilter() throws Exception {
+ void testRemoveNonExistingFilterFromSingleSimpleFilter() {
final Filter filter = ThresholdFilter.createFilter(Level.ERROR, null, null);
final Filter newFilter = ThresholdFilter.createFilter(Level.WARN, null, null);
@@ -197,7 +197,7 @@ public void testRemoveNonExistingFilterFromSingleSimpleFilter() throws Exception
}
@Test
- public void testRemoveSimpleFilterFromCompositeFilter() {
+ void testRemoveSimpleFilterFromCompositeFilter() {
final Filter filter1 = ThresholdFilter.createFilter(Level.ERROR, null, null);
final Filter filter2 = ThresholdFilter.createFilter(Level.ERROR, null, null);
final Filter compositeFilter = CompositeFilter.createFilters(new Filter[] {filter1, filter2});
@@ -212,7 +212,7 @@ public void testRemoveSimpleFilterFromCompositeFilter() {
}
@Test
- public void testRemoveSimpleFilterFromCompositeAndSimpleFilter() {
+ void testRemoveSimpleFilterFromCompositeAndSimpleFilter() {
final Filter filter1 = ThresholdFilter.createFilter(Level.ERROR, null, null);
final Filter filter2 = ThresholdFilter.createFilter(Level.ERROR, null, null);
final Filter compositeFilter = CompositeFilter.createFilters(new Filter[] {filter1, filter2});
@@ -228,7 +228,7 @@ public void testRemoveSimpleFilterFromCompositeAndSimpleFilter() {
}
@Test
- public void testRemoveCompositeFilterFromCompositeFilter() {
+ void testRemoveCompositeFilterFromCompositeFilter() {
final Filter filter1 = ThresholdFilter.createFilter(Level.ERROR, null, null);
final Filter filter2 = ThresholdFilter.createFilter(Level.ERROR, null, null);
final Filter compositeFilter = CompositeFilter.createFilters(new Filter[] {filter1, filter2});
@@ -239,7 +239,7 @@ public void testRemoveCompositeFilterFromCompositeFilter() {
}
@Test
- public void testRemoveFiltersFromComposite() {
+ void testRemoveFiltersFromComposite() {
final Filter filter1 = ThresholdFilter.createFilter(Level.ERROR, null, null);
final Filter filter2 = ThresholdFilter.createFilter(Level.ERROR, null, null);
final Filter compositeFilter = CompositeFilter.createFilters(new Filter[] {filter1, filter2});
From e6a5ee39aa689d9742201f1ace0a364bd009fadb Mon Sep 17 00:00:00 2001
From: Jeff Thomas
Date: Sun, 2 Mar 2025 23:41:43 +0100
Subject: [PATCH 12/12] Renamed and updated changelog. (#3086)
---
...luginFactory.xml => 3086_change_RegexFilter_cleanup_API.xml} | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
rename src/changelog/.3.x.x/{3086_change_RegexFilter_remove_patternflags_from_PluginFactory.xml => 3086_change_RegexFilter_cleanup_API.xml} (80%)
diff --git a/src/changelog/.3.x.x/3086_change_RegexFilter_remove_patternflags_from_PluginFactory.xml b/src/changelog/.3.x.x/3086_change_RegexFilter_cleanup_API.xml
similarity index 80%
rename from src/changelog/.3.x.x/3086_change_RegexFilter_remove_patternflags_from_PluginFactory.xml
rename to src/changelog/.3.x.x/3086_change_RegexFilter_cleanup_API.xml
index 0e61653f85c..e503826efe3 100644
--- a/src/changelog/.3.x.x/3086_change_RegexFilter_remove_patternflags_from_PluginFactory.xml
+++ b/src/changelog/.3.x.x/3086_change_RegexFilter_cleanup_API.xml
@@ -5,6 +5,6 @@
type="fixed">
- Removed 'patternFlags' @PluginAttribute from RegexFilter @PluginFactory createFilter.
+ Reworked API for RegexFilter: added getXXX for configuration attributes, removed deprecated API.