From f0ec0b8ba757e7e137ef91762ac8764801d16eec Mon Sep 17 00:00:00 2001
From: Jeff Thomas
Date: Wed, 12 Feb 2025 14:07:22 +0100
Subject: [PATCH 01/42] Removed 'patternFlags' @PluginAttribute from
RegexFilter @PluginFactory createFilter. (#3086)
---
.../log4j/core/filter/RegexFilter.java | 77 +++++++++++--------
...remove_patternflags_from_PluginFactory.xml | 10 +++
2 files changed, 53 insertions(+), 34 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 62d41b31f59..84cdbfad47b 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;
@@ -29,7 +26,6 @@
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.message.Message;
import org.apache.logging.log4j.message.MessageFormatMessage;
@@ -43,7 +39,6 @@
@Plugin(name = "RegexFilter", category = Node.CATEGORY, elementType = Filter.ELEMENT_TYPE, printObject = true)
public final class RegexFilter extends AbstractFilter {
- private static final int DEFAULT_PATTERN_FLAGS = 0;
private final Pattern pattern;
private final boolean useRawMessage;
@@ -110,10 +105,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();
}
/**
@@ -123,6 +115,40 @@ 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 {@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.
+ * @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
@@ -138,40 +164,23 @@ public String toString() {
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 {
+ boolean raw = Boolean.TRUE.equals(useRawMsg);
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)
- 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 8c0e3c6c4f32ba97985efc05286e5425bfe36742 Mon Sep 17 00:00:00 2001
From: Suvrat Acharya <140749446+Suvrat1629@users.noreply.github.com>
Date: Sun, 16 Feb 2025 13:59:16 +0530
Subject: [PATCH 02/42] Improve configuration error handling of HttpAppender
(#3438)
This PR introduces improvements to `HttpAppender` and adds a new test class, `HttpAppenderBuilderTest`, to enhance test coverage.
The changes include:
* Updating `HttpAppender` to improve validating behavior.
* Adding HttpAppenderBuilderTest.java to verify the builder logic for HttpAppender.
Ensuring that missing configurations (e.g., URL, Layout) correctly log errors.
Co-authored-by: Piotr P. Karwasz
---
.../appender/HttpAppenderBuilderTest.java | 130 ++++++++++++++++++
.../log4j/core/appender/HttpAppender.java | 24 +++-
.../.2.x.x/3011_http_appender_validation.xml | 8 ++
3 files changed, 155 insertions(+), 7 deletions(-)
create mode 100644 log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/HttpAppenderBuilderTest.java
create mode 100644 src/changelog/.2.x.x/3011_http_appender_validation.xml
diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/HttpAppenderBuilderTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/HttpAppenderBuilderTest.java
new file mode 100644
index 00000000000..2680961d19c
--- /dev/null
+++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/HttpAppenderBuilderTest.java
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.logging.log4j.core.appender;
+
+import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.DefaultConfiguration;
+import org.apache.logging.log4j.core.config.Property;
+import org.apache.logging.log4j.core.layout.JsonLayout;
+import org.apache.logging.log4j.core.net.ssl.SslConfiguration;
+import org.apache.logging.log4j.test.ListStatusListener;
+import org.apache.logging.log4j.test.junit.UsingStatusListener;
+import org.junit.jupiter.api.Test;
+
+class HttpAppenderBuilderTest {
+
+ private HttpAppender.Builder> getBuilder() {
+ Configuration mockConfig = new DefaultConfiguration();
+ return HttpAppender.newBuilder().setConfiguration(mockConfig).setName("TestHttpAppender"); // Name is required
+ }
+
+ @Test
+ @UsingStatusListener
+ void testBuilderWithoutUrl(final ListStatusListener listener) throws Exception {
+ HttpAppender appender = HttpAppender.newBuilder()
+ .setConfiguration(new DefaultConfiguration())
+ .setName("TestAppender")
+ .setLayout(JsonLayout.createDefaultLayout()) // Providing a layout here
+ .build();
+
+ assertThat(listener.findStatusData(Level.ERROR))
+ .anyMatch(statusData ->
+ statusData.getMessage().getFormattedMessage().contains("HttpAppender requires URL to be set."));
+ }
+
+ @Test
+ @UsingStatusListener
+ void testBuilderWithUrlAndWithoutLayout(final ListStatusListener listener) throws Exception {
+ HttpAppender appender = HttpAppender.newBuilder()
+ .setConfiguration(new DefaultConfiguration())
+ .setName("TestAppender")
+ .setUrl(new URL("http://localhost:8080/logs"))
+ .build();
+
+ assertThat(listener.findStatusData(Level.ERROR)).anyMatch(statusData -> statusData
+ .getMessage()
+ .getFormattedMessage()
+ .contains("HttpAppender requires a layout to be set."));
+ }
+
+ @Test
+ void testBuilderWithValidConfiguration() throws Exception {
+ URL url = new URL("http://example.com");
+ Layout> layout = JsonLayout.createDefaultLayout();
+
+ HttpAppender.Builder> builder = getBuilder().setUrl(url).setLayout(layout);
+
+ HttpAppender appender = builder.build();
+ assertNotNull(appender, "HttpAppender should be created with valid configuration.");
+ }
+
+ @Test
+ void testBuilderWithCustomMethod() throws Exception {
+ URL url = new URL("http://example.com");
+ Layout> layout = JsonLayout.createDefaultLayout();
+ String customMethod = "PUT";
+
+ HttpAppender.Builder> builder =
+ getBuilder().setUrl(url).setLayout(layout).setMethod(customMethod);
+
+ HttpAppender appender = builder.build();
+ assertNotNull(appender, "HttpAppender should be created with a custom HTTP method.");
+ }
+
+ @Test
+ void testBuilderWithHeaders() throws Exception {
+ URL url = new URL("http://example.com");
+ Layout> layout = JsonLayout.createDefaultLayout();
+ Property[] headers = new Property[] {
+ Property.createProperty("Header1", "Value1"), Property.createProperty("Header2", "Value2")
+ };
+
+ HttpAppender.Builder> builder =
+ getBuilder().setUrl(url).setLayout(layout).setHeaders(headers);
+
+ HttpAppender appender = builder.build();
+ assertNotNull(appender, "HttpAppender should be created with headers.");
+ }
+
+ @Test
+ void testBuilderWithSslConfiguration() throws Exception {
+ URL url = new URL("https://example.com");
+ Layout> layout = JsonLayout.createDefaultLayout();
+
+ // Use real SslConfiguration instead of Mockito mock
+ SslConfiguration sslConfig = SslConfiguration.createSSLConfiguration(null, null, null, false);
+
+ HttpAppender.Builder> builder =
+ getBuilder().setUrl(url).setLayout(layout).setSslConfiguration(sslConfig);
+
+ HttpAppender appender = builder.build();
+ assertNotNull(appender, "HttpAppender should be created with SSL configuration.");
+ }
+
+ @Test
+ void testBuilderWithInvalidUrl() {
+ assertThrows(MalformedURLException.class, () -> new URL("invalid-url"));
+ }
+}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/HttpAppender.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/HttpAppender.java
index b24e165b3e1..56d1ffb5e37 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/HttpAppender.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/HttpAppender.java
@@ -32,16 +32,15 @@
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.net.ssl.SslConfiguration;
+import org.apache.logging.log4j.status.StatusLogger;
-/**
- * Sends log events over HTTP.
- */
@Plugin(name = "Http", category = Node.CATEGORY, elementType = Appender.ELEMENT_TYPE, printObject = true)
public final class HttpAppender extends AbstractAppender {
+ private static final StatusLogger LOGGER = StatusLogger.getLogger();
+
/**
* Builds HttpAppender instances.
- * @param The type to build
*/
public static class Builder> extends AbstractAppender.Builder
implements org.apache.logging.log4j.core.util.Builder {
@@ -70,6 +69,18 @@ public static class Builder> extends AbstractAppender.Build
@Override
public HttpAppender build() {
+ // Validate URL presence
+ if (url == null) {
+ LOGGER.error("HttpAppender requires URL to be set.");
+ return null; // Return null if URL is missing
+ }
+
+ // Validate layout presence
+ if (getLayout() == null) {
+ LOGGER.error("HttpAppender requires a layout to be set.");
+ return null; // Return null if layout is missing
+ }
+
final HttpManager httpManager = new HttpURLConnectionManager(
getConfiguration(),
getConfiguration().getLoggerContext(),
@@ -81,10 +92,12 @@ public HttpAppender build() {
headers,
sslConfiguration,
verifyHostname);
+
return new HttpAppender(
getName(), getLayout(), getFilter(), isIgnoreExceptions(), httpManager, getPropertyArray());
}
+ // Getter and Setter methods
public URL getUrl() {
return url;
}
@@ -149,9 +162,6 @@ public B setVerifyHostname(final boolean verifyHostname) {
}
}
- /**
- * @return a builder for a HttpAppender.
- */
@PluginBuilderFactory
public static > B newBuilder() {
return new Builder().asBuilder();
diff --git a/src/changelog/.2.x.x/3011_http_appender_validation.xml b/src/changelog/.2.x.x/3011_http_appender_validation.xml
new file mode 100644
index 00000000000..5038a378d17
--- /dev/null
+++ b/src/changelog/.2.x.x/3011_http_appender_validation.xml
@@ -0,0 +1,8 @@
+
+
+
+ Improves validation of HTTP Appender.
+
From dd7b6d30c612212b9f3959e26fce0637222cacb5 Mon Sep 17 00:00:00 2001
From: Jeff Thomas
Date: Mon, 17 Feb 2025 17:05:26 +0100
Subject: [PATCH 03/42] 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 | 11 +-
.../log4j/core/filter/AbstractFilter.java | 33 +-
.../log4j/core/filter/RegexFilter.java | 358 ++++++++++++++----
.../logging/log4j/core/util/Builder.java | 4 +
...remove_patternflags_from_PluginFactory.xml | 0
5 files changed, 328 insertions(+), 78 deletions(-)
rename src/changelog/{2.25.0 => .2.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 671d998258b..e5801342dd6 100644
--- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/RegexFilterTest.java
+++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/RegexFilterTest.java
@@ -19,6 +19,7 @@
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;
@@ -43,13 +44,17 @@ static void before() {
@Test
void testRegexFilterDoesNotThrowWithAllTheParametersExceptRegexEqualNull() {
assertDoesNotThrow(() -> {
- RegexFilter.createFilter(".* test .*", null, null, null, null);
+ RegexFilter.newBuilder().setRegex(".* test .*").build();
});
}
@Test
void testThresholds() throws Exception {
- RegexFilter filter = RegexFilter.createFilter(".* test .*", null, false, null, null);
+ RegexFilter filter = RegexFilter.newBuilder()
+ .setRegex(".* test .*")
+ .setUseRawMsg(false)
+ .build();
+ assertNotNull(filter);
filter.start();
assertTrue(filter.isStarted());
assertSame(
@@ -65,7 +70,7 @@ void testThresholds() throws Exception {
.setMessage(new SimpleMessage("test")) //
.build();
assertSame(Filter.Result.DENY, filter.filter(event));
- filter = RegexFilter.createFilter(null, null, false, null, null);
+ filter = RegexFilter.newBuilder().build();
assertNull(filter);
}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/AbstractFilter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/AbstractFilter.java
index 397390bcbc3..05b1e6b275b 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/AbstractFilter.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/AbstractFilter.java
@@ -16,6 +16,8 @@
*/
package org.apache.logging.log4j.core.filter;
+import java.util.Objects;
+import java.util.Optional;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.core.AbstractLifeCycle;
@@ -43,16 +45,30 @@ public abstract static class AbstractFilterBuilder builder) {
+
+ Objects.requireNonNull(builder, "The 'builder' argument cannot be null.");
+
+ this.onMatch = Optional.ofNullable(builder.onMatch).orElse(Result.NEUTRAL);
+ this.onMismatch = Optional.ofNullable(builder.onMismatch).orElse(Result.DENY);
+ }
+
@Override
protected boolean equalsImpl(final Object obj) {
if (this == obj) {
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/RegexFilter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/RegexFilter.java
index 84cdbfad47b..d26d157bcc7 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.MessageFormatMessage;
import org.apache.logging.log4j.message.ParameterizedMessage;
@@ -39,53 +44,132 @@
@Plugin(name = "RegexFilter", category = Node.CATEGORY, elementType = Filter.ELEMENT_TYPE, printObject = true)
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 = targetMessageTest(msg);
- return filter(text);
+ return filter(getMessageTextByType(msg));
}
+ /** {@inheritDoc} */
@Override
public Result filter(final LogEvent event) {
- final String text = targetMessageTest(event.getMessage());
- 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;
+ }
+ return pattern.matcher(msg).matches() ? onMatch : onMismatch;
}
- // While `Message#getFormat()` is broken in general, it still makes sense for certain types.
- // Hence, suppress the deprecation warning.
+ /**
+ * Tests the filter pattern against the given Log4j {@code Message}.
+ *
+ * If the raw-message flag is enabled and message is an instance of the following, the raw message format
+ * will be returned.
+ *
+ *
+ * - {@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 targetMessageTest(final Message message) {
+ private String getMessageTextByType(final Message message) {
return useRawMessage
&& (message instanceof ParameterizedMessage
|| message instanceof StringFormattedMessage
@@ -95,92 +179,220 @@ private String targetMessageTest(final Message message) {
: message.getFormattedMessage();
}
- private Result filter(final String msg) {
- if (msg == null) {
- return onMismatch;
- }
- final Matcher m = pattern.matcher(msg);
- return m.matches() ? onMatch : onMismatch;
- }
-
@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 {@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.
- * @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];
- return createFilter(regex, useRawMsg, match, mismatch);
+ /**
+ * @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;
+ }
+
+ /**
+ * 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 10bc1a9f52e..8a772dc7330 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Builder.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Builder.java
@@ -44,6 +44,10 @@ public interface Builder {
*/
T build();
+ /**
+ * Validates that the builder is properly configured to build.
+ * @return {@code true} if the builder configuration is valid; otherwise, {@code false}
+ */
default boolean isValid() {
return PluginBuilder.validateFields(this, getErrorPrefix());
}
diff --git a/src/changelog/2.25.0/3086_change_RegexFilter_remove_patternflags_from_PluginFactory.xml b/src/changelog/.2.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/.2.x.x/3086_change_RegexFilter_remove_patternflags_from_PluginFactory.xml
From 29e472b15ee366925a4292c7676be2482b1787cd Mon Sep 17 00:00:00 2001
From: Jeff Thomas
Date: Mon, 17 Feb 2025 17:47:17 +0100
Subject: [PATCH 04/42] 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 e5801342dd6..6cb07a5f7df 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;
@@ -87,9 +90,18 @@ void testDotAllPattern() throws Exception {
}
@Test
- void testNoMsg() throws Exception {
- final RegexFilter filter = RegexFilter.createFilter(".* test .*", null, false, null, null);
+ void testNoMsg() {
+
+ final RegexFilter filter =
+ RegexFilter.newBuilder()
+ .setRegex(".* test .*")
+ .setUseRawMsg(false)
+ .build();
+
+ assertNotNull(filter);
+
filter.start();
+
assertTrue(filter.isStarted());
assertSame(Filter.Result.DENY, filter.filter(null, Level.DEBUG, null, (Object) null, null));
assertSame(Filter.Result.DENY, filter.filter(null, Level.DEBUG, null, (Message) null, null));
@@ -97,28 +109,112 @@ void testNoMsg() throws Exception {
}
@Test
- void testParameterizedMsg() throws Exception {
+ void testParameterizedMsg() {
final String msg = "params {} {}";
final Object[] params = {"foo", "bar"};
// match against raw message
- final RegexFilter rawFilter = RegexFilter.createFilter(
- "params \\{\\} \\{\\}",
- null,
- true, // useRawMsg
- Result.ACCEPT,
- Result.DENY);
+ final RegexFilter rawFilter =
+ RegexFilter.newBuilder()
+ .setRegex("params \\{\\} \\{\\}")
+ .setUseRawMsg(true)
+ .setOnMatch(Result.ACCEPT)
+ .setOnMismatch(Result.DENY)
+ .build();
+
+ assertNotNull(rawFilter);
+
final Result rawResult = rawFilter.filter(null, null, null, msg, params);
assertThat(rawResult, equalTo(Result.ACCEPT));
// match against formatted message
- final RegexFilter fmtFilter = RegexFilter.createFilter(
- "params foo bar",
- null,
- false, // useRawMsg
- Result.ACCEPT,
- Result.DENY);
+ final RegexFilter fmtFilter =
+ RegexFilter.newBuilder()
+ .setRegex("params foo bar")
+ .setUseRawMsg(false)
+ .setOnMatch(Result.ACCEPT)
+ .setOnMismatch(Result.DENY).build();
+
+ assertNotNull(fmtFilter);
+
final Result fmtResult = fmtFilter.filter(null, null, null, msg, params);
assertThat(fmtResult, equalTo(Result.ACCEPT));
}
+
+ /**
+ * A builder with no 'regex' expression should both be invalid and return null on 'build()'.
+ */
+ @Test
+ void testWithValidRegex() {
+
+ final String regex = "^[a-zA-Z0-9_]+$"; // matches alphanumeric with underscores
+
+ final RegexFilter.Builder builder =
+ RegexFilter.newBuilder().setRegex(regex).setUseRawMsg(false).setOnMatch(Result.ACCEPT).setOnMismatch(Result.DENY);
+
+ 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 d26d157bcc7..6c842e66593 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;
@@ -44,9 +45,6 @@
@Plugin(name = "RegexFilter", category = Node.CATEGORY, elementType = Filter.ELEMENT_TYPE, printObject = true)
public final class RegexFilter extends AbstractFilter {
- /** The regular-expression. */
- private final String regex;
-
/** The pattern compiled from the regular-expression. */
private final Pattern pattern;
@@ -62,13 +60,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);
}
}
@@ -77,7 +74,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();
}
/**
@@ -136,7 +133,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;
}
@@ -181,7 +178,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();
}
/**
@@ -251,7 +248,11 @@ public Builder setUseRawMsg(final boolean useRawMsg) {
*/
@Override
public boolean isValid() {
- return (regex != null);
+ boolean valid = true;
+ if (!isRegexValid()) {
+ valid = false;
+ }
+ return valid;
}
/**
@@ -273,6 +274,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;
+ }
}
/*
@@ -306,7 +326,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 85c6c9b8cf18c1458e3abb5ff60bf233f4acf85b Mon Sep 17 00:00:00 2001
From: Clay Johnson
Date: Tue, 18 Feb 2025 13:58:49 -0600
Subject: [PATCH 05/42] Publish build scans to develocity.apache.org (#3396)
* Publish build scans to develocity.apache.org
* Use `DEVELOCITY_ACCESS_KEY` to authenticate to `develocity.apache.org`
---
.github/workflows/build.yaml | 2 +-
.github/workflows/develocity-publish-build-scans.yaml | 4 ++--
.github/workflows/merge-dependabot.yaml | 2 +-
.mvn/develocity.xml | 2 +-
pom.xml | 2 +-
5 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index ac7d6128ffc..bf3be134349 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -32,7 +32,7 @@ jobs:
if: github.actor != 'dependabot[bot]'
uses: apache/logging-parent/.github/workflows/build-reusable.yaml@rel/11.3.0
secrets:
- DV_ACCESS_TOKEN: ${{ startsWith(github.ref_name, 'release/') && '' || secrets.GE_ACCESS_TOKEN }}
+ DV_ACCESS_TOKEN: ${{ startsWith(github.ref_name, 'release/') && '' || secrets.DEVELOCITY_ACCESS_KEY }}
with:
java-version: |
8
diff --git a/.github/workflows/develocity-publish-build-scans.yaml b/.github/workflows/develocity-publish-build-scans.yaml
index 78069943bb0..31fa075ac2f 100644
--- a/.github/workflows/develocity-publish-build-scans.yaml
+++ b/.github/workflows/develocity-publish-build-scans.yaml
@@ -38,5 +38,5 @@ jobs:
- name: Publish Build Scans
uses: gradle/develocity-actions/maven-publish-build-scan@b8d3a572314ffff3b940a2c1b7b384d4983d422d # 1.3
with:
- develocity-url: 'https://ge.apache.org'
- develocity-access-key: ${{ secrets.GE_ACCESS_TOKEN }}
+ develocity-url: 'https://develocity.apache.org'
+ develocity-access-key: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
diff --git a/.github/workflows/merge-dependabot.yaml b/.github/workflows/merge-dependabot.yaml
index 4e35def4b67..c81076e1860 100644
--- a/.github/workflows/merge-dependabot.yaml
+++ b/.github/workflows/merge-dependabot.yaml
@@ -32,7 +32,7 @@ jobs:
if: github.repository == 'apache/logging-log4j2' && github.event_name == 'pull_request_target' && github.actor == 'dependabot[bot]'
uses: apache/logging-parent/.github/workflows/build-reusable.yaml@rel/11.3.0
secrets:
- DV_ACCESS_TOKEN: ${{ secrets.GE_ACCESS_TOKEN }}
+ DV_ACCESS_TOKEN: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
with:
java-version: |
8
diff --git a/.mvn/develocity.xml b/.mvn/develocity.xml
index 84def1dec34..caa112766a0 100644
--- a/.mvn/develocity.xml
+++ b/.mvn/develocity.xml
@@ -2,7 +2,7 @@
logging-log4j2
- https://ge.apache.org
+ https://develocity.apache.org
diff --git a/pom.xml b/pom.xml
index bcc6f1b9c75..41283036545 100644
--- a/pom.xml
+++ b/pom.xml
@@ -370,7 +370,7 @@
${maven.multiModuleProjectDirectory}/target/plugin-descriptors/phase1
${maven.multiModuleProjectDirectory}/target/plugin-descriptors/phase2
-
+
3.2.5
From fef8af8e66981bf32933b2158cafeabc3305c2d0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?=
Date: Tue, 18 Feb 2025 22:06:41 +0100
Subject: [PATCH 06/42] Fix null termination advice for SOA and JTL
---
.../antora/modules/ROOT/examples/cloud/logstash/log4j2.json | 4 +---
.../modules/ROOT/examples/cloud/logstash/log4j2.properties | 1 -
.../antora/modules/ROOT/examples/cloud/logstash/log4j2.xml | 2 +-
.../antora/modules/ROOT/examples/cloud/logstash/log4j2.yaml | 3 +--
src/site/antora/modules/ROOT/pages/soa.adoc | 3 +--
5 files changed, 4 insertions(+), 9 deletions(-)
diff --git a/src/site/antora/modules/ROOT/examples/cloud/logstash/log4j2.json b/src/site/antora/modules/ROOT/examples/cloud/logstash/log4j2.json
index 14512de6346..3ebdb0994eb 100644
--- a/src/site/antora/modules/ROOT/examples/cloud/logstash/log4j2.json
+++ b/src/site/antora/modules/ROOT/examples/cloud/logstash/log4j2.json
@@ -6,9 +6,7 @@
"name": "SOCKET",
"host": "localhost",
"port": 12345,
- "JsonTemplateLayout": {
- "nullEventDelimiterEnabled": true
- }
+ "JsonTemplateLayout": {}
}
// end::socketAppender[]
},
diff --git a/src/site/antora/modules/ROOT/examples/cloud/logstash/log4j2.properties b/src/site/antora/modules/ROOT/examples/cloud/logstash/log4j2.properties
index b3aa633b9f2..c25e660b87a 100644
--- a/src/site/antora/modules/ROOT/examples/cloud/logstash/log4j2.properties
+++ b/src/site/antora/modules/ROOT/examples/cloud/logstash/log4j2.properties
@@ -21,7 +21,6 @@ appender.0.name = SOCKET
appender.0.host = localhost
appender.0.port = 12345
appender.0.layout.type = JsonTemplateLayout
-appender.0.layout.nullEventDelimiterEnabled = true
# end::socketAppender[]
rootLogger.level = WARN
diff --git a/src/site/antora/modules/ROOT/examples/cloud/logstash/log4j2.xml b/src/site/antora/modules/ROOT/examples/cloud/logstash/log4j2.xml
index bc7e6e3697e..13b960ec2b9 100644
--- a/src/site/antora/modules/ROOT/examples/cloud/logstash/log4j2.xml
+++ b/src/site/antora/modules/ROOT/examples/cloud/logstash/log4j2.xml
@@ -24,7 +24,7 @@
-
+
diff --git a/src/site/antora/modules/ROOT/examples/cloud/logstash/log4j2.yaml b/src/site/antora/modules/ROOT/examples/cloud/logstash/log4j2.yaml
index 60cc47d586d..d6e0a44787c 100644
--- a/src/site/antora/modules/ROOT/examples/cloud/logstash/log4j2.yaml
+++ b/src/site/antora/modules/ROOT/examples/cloud/logstash/log4j2.yaml
@@ -22,8 +22,7 @@ Configuration:
name: "SOCKET"
host: "localhost"
port: 12345
- JsonTemplateLayout:
- nullEventDelimiterEnabled: true
+ JsonTemplateLayout: {}
# end::socketAppender[]
Loggers:
diff --git a/src/site/antora/modules/ROOT/pages/soa.adoc b/src/site/antora/modules/ROOT/pages/soa.adoc
index 5db72c82478..063909ae495 100644
--- a/src/site/antora/modules/ROOT/pages/soa.adoc
+++ b/src/site/antora/modules/ROOT/pages/soa.adoc
@@ -99,8 +99,7 @@ Nevertheless, there are some tips we recommend you to practice:
* *For writing to console*, use a xref:manual/appenders.adoc#ConsoleAppender[Console Appender] and make sure to configure its `direct` attribute to `true` for the maximum efficiency.
-* *For writing to an external service*, use a xref:manual/appenders/network.adoc#SocketAppender[Socket Appender] and make sure to set the protocol to TCP and configure the null delimiter of the associated layout.
-For instance, see xref:manual/json-template-layout.adoc#plugin-attr-nullEventDelimiterEnabled[the `nullEventDelimiterEnabled` configuration attribute of JSON Template Layout].
+* *For writing to an external service*, use a xref:manual/appenders/network.adoc#SocketAppender[Socket Appender] and make sure to configure the protocol and layout's null termination (e.g., see xref:manual/json-template-layout.adoc#plugin-attr-nullEventDelimiterEnabled[the `nullEventDelimiterEnabled` configuration attribute of JSON Template Layout]) appropriately.
[#file]
=== Avoid writing to files
From 55b799bbafd03503fa20dfcfa5c1ff7ad63fa360 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 18 Feb 2025 22:36:24 +0100
Subject: [PATCH 07/42] Bump org.apache.logging:logging-parent from 11.3.0 to
12.0.0 in /log4j-parent (#3452)
* Bump org.apache.logging:logging-parent in /log4j-parent
Bumps [org.apache.logging:logging-parent](https://github.com/apache/logging-parent) from 11.3.0 to 12.0.0.
- [Release notes](https://github.com/apache/logging-parent/releases)
- [Commits](https://github.com/apache/logging-parent/compare/rel/11.3.0...rel/12.0.0)
---
updated-dependencies:
- dependency-name: org.apache.logging:logging-parent
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot]
* Necessary fixes for `12.0.0` upgrade.
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Piotr P. Karwasz
---
.github/workflows/build.yaml | 6 ++--
.github/workflows/codeql-analysis.yaml | 2 +-
.github/workflows/deploy-site.yaml | 6 ++--
.github/workflows/merge-dependabot.yaml | 4 +--
log4j-parent/pom.xml | 28 +++++++++++++++++--
pom.xml | 4 ++-
...date_org_apache_logging_logging_parent.xml | 2 +-
7 files changed, 39 insertions(+), 13 deletions(-)
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index bf3be134349..e4046edb7c3 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -30,7 +30,7 @@ jobs:
build:
if: github.actor != 'dependabot[bot]'
- uses: apache/logging-parent/.github/workflows/build-reusable.yaml@rel/11.3.0
+ uses: apache/logging-parent/.github/workflows/build-reusable.yaml@rel/12.0.0
secrets:
DV_ACCESS_TOKEN: ${{ startsWith(github.ref_name, 'release/') && '' || secrets.DEVELOCITY_ACCESS_KEY }}
with:
@@ -44,7 +44,7 @@ jobs:
deploy-snapshot:
needs: build
if: github.repository == 'apache/logging-log4j2' && github.ref_name == '2.x'
- uses: apache/logging-parent/.github/workflows/deploy-snapshot-reusable.yaml@rel/11.3.0
+ uses: apache/logging-parent/.github/workflows/deploy-snapshot-reusable.yaml@rel/12.0.0
# Secrets for deployments
secrets:
NEXUS_USERNAME: ${{ secrets.NEXUS_USER }}
@@ -57,7 +57,7 @@ jobs:
deploy-release:
needs: build
if: github.repository == 'apache/logging-log4j2' && startsWith(github.ref_name, 'release/')
- uses: apache/logging-parent/.github/workflows/deploy-release-reusable.yaml@rel/11.3.0
+ uses: apache/logging-parent/.github/workflows/deploy-release-reusable.yaml@rel/12.0.0
# Secrets for deployments
secrets:
GPG_SECRET_KEY: ${{ secrets.LOGGING_GPG_SECRET_KEY }}
diff --git a/.github/workflows/codeql-analysis.yaml b/.github/workflows/codeql-analysis.yaml
index cc2dbcaaf85..05a37e02f99 100644
--- a/.github/workflows/codeql-analysis.yaml
+++ b/.github/workflows/codeql-analysis.yaml
@@ -30,7 +30,7 @@ permissions: read-all
jobs:
analyze:
- uses: apache/logging-parent/.github/workflows/codeql-analysis-reusable.yaml@rel/11.3.0
+ uses: apache/logging-parent/.github/workflows/codeql-analysis-reusable.yaml@rel/12.0.0
with:
java-version: |
8
diff --git a/.github/workflows/deploy-site.yaml b/.github/workflows/deploy-site.yaml
index 365b8fc2901..604ac5f63aa 100644
--- a/.github/workflows/deploy-site.yaml
+++ b/.github/workflows/deploy-site.yaml
@@ -33,7 +33,7 @@ jobs:
deploy-site-stg:
if: github.repository == 'apache/logging-log4j2' && github.ref_name == '2.x'
- uses: apache/logging-parent/.github/workflows/deploy-site-reusable.yaml@rel/11.3.0
+ uses: apache/logging-parent/.github/workflows/deploy-site-reusable.yaml@rel/12.0.0
# Secrets for committing the generated site
secrets:
GPG_SECRET_KEY: ${{ secrets.LOGGING_GPG_SECRET_KEY }}
@@ -51,7 +51,7 @@ jobs:
deploy-site-pro:
if: github.repository == 'apache/logging-log4j2' && github.ref_name == '2.x-site-pro'
- uses: apache/logging-parent/.github/workflows/deploy-site-reusable.yaml@rel/11.3.0
+ uses: apache/logging-parent/.github/workflows/deploy-site-reusable.yaml@rel/12.0.0
# Secrets for committing the generated site
secrets:
GPG_SECRET_KEY: ${{ secrets.LOGGING_GPG_SECRET_KEY }}
@@ -81,7 +81,7 @@ jobs:
deploy-site-rel:
needs: export-version
- uses: apache/logging-parent/.github/workflows/deploy-site-reusable.yaml@rel/11.3.0
+ uses: apache/logging-parent/.github/workflows/deploy-site-reusable.yaml@rel/12.0.0
# Secrets for committing the generated site
secrets:
GPG_SECRET_KEY: ${{ secrets.LOGGING_GPG_SECRET_KEY }}
diff --git a/.github/workflows/merge-dependabot.yaml b/.github/workflows/merge-dependabot.yaml
index c81076e1860..cfb0b1d63dd 100644
--- a/.github/workflows/merge-dependabot.yaml
+++ b/.github/workflows/merge-dependabot.yaml
@@ -30,7 +30,7 @@ jobs:
build:
if: github.repository == 'apache/logging-log4j2' && github.event_name == 'pull_request_target' && github.actor == 'dependabot[bot]'
- uses: apache/logging-parent/.github/workflows/build-reusable.yaml@rel/11.3.0
+ uses: apache/logging-parent/.github/workflows/build-reusable.yaml@rel/12.0.0
secrets:
DV_ACCESS_TOKEN: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
with:
@@ -42,7 +42,7 @@ jobs:
merge-dependabot:
needs: build
- uses: apache/logging-parent/.github/workflows/merge-dependabot-reusable.yaml@rel/11.3.0
+ uses: apache/logging-parent/.github/workflows/merge-dependabot-reusable.yaml@rel/12.0.0
with:
java-version: 17
permissions:
diff --git a/log4j-parent/pom.xml b/log4j-parent/pom.xml
index c429feaf95a..d72e426442c 100644
--- a/log4j-parent/pom.xml
+++ b/log4j-parent/pom.xml
@@ -66,6 +66,7 @@
3.27.3
4.2.2
2.0b6
+ 7.1.0
3.11.18
3.11.5
1.17.1
@@ -125,8 +126,11 @@
2.0.8
6.0.0
+ 2.0.0
+ 1.1.2
4.14.0
3.6.0
+ 4.9.1
2.7.18
5.3.39
2.0.3
@@ -574,6 +578,12 @@
${jna.version}
+
+ org.jspecify
+ jspecify
+ ${jspecify.version}
+
+
junit
junit
@@ -829,6 +839,7 @@
biz.aQute.bnd
biz.aQute.bnd.annotation
+ ${bnd.annotation.version}
provided
@@ -841,19 +852,22 @@
org.osgi
- osgi.annotation
+ org.osgi.annotation.bundle
+ ${osgi.annotation.bundle.version}
provided
org.osgi
- org.osgi.annotation.bundle
+ org.osgi.annotation.versioning
+ ${osgi.annotation.versioning.version}
provided
com.github.spotbugs
spotbugs-annotations
+ ${spotbugs-annotations.version}
provided
@@ -977,6 +991,16 @@
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ --should-stop=ifError=FLOW
+
+
+
+
diff --git a/pom.xml b/pom.xml
index 41283036545..177b0531d2c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -31,7 +31,7 @@
org.apache.logging
logging-parent
- 11.3.0
+ 12.0.0
@@ -357,6 +357,7 @@
+ 1.0.0
0.9.0
21.7.1
10.5.0
@@ -993,6 +994,7 @@
org.jspecify
jspecify
${jspecify.version}
+ test
diff --git a/src/changelog/.2.x.x/update_org_apache_logging_logging_parent.xml b/src/changelog/.2.x.x/update_org_apache_logging_logging_parent.xml
index 5b17082960b..0ea7b563939 100644
--- a/src/changelog/.2.x.x/update_org_apache_logging_logging_parent.xml
+++ b/src/changelog/.2.x.x/update_org_apache_logging_logging_parent.xml
@@ -3,5 +3,5 @@
xmlns="https://logging.apache.org/xml/ns"
xsi:schemaLocation="https://logging.apache.org/xml/ns https://logging.apache.org/xml/ns/log4j-changelog-0.xsd"
type="updated">
- Update `org.apache.logging:logging-parent` to version `11.3.0`
+ Update `org.apache.logging:logging-parent` to version `12.0.0`
From 382ea9b6194d85b9cd39efebfafec40735200335 Mon Sep 17 00:00:00 2001
From: "Piotr P. Karwasz"
Date: Thu, 17 Oct 2024 12:11:24 +0200
Subject: [PATCH 08/42] Run reproducibility check after each deployment
This PR starts a separate `verify-reproducibility` job, whenever a snapshot or release is deployed.
---
.github/workflows/build.yaml | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index e4046edb7c3..721a7f8a375 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -38,7 +38,7 @@ jobs:
8
17
site-enabled: true
- reproducibility-check-enabled: ${{ startsWith(github.ref_name, 'release/') }}
+ reproducibility-check-enabled: false
develocity-enabled: ${{ ! startsWith(github.ref_name, 'release/') }}
deploy-snapshot:
@@ -73,3 +73,14 @@ jobs:
8
17
project-id: log4j
+
+ verify-reproducibility:
+ needs: [ deploy-snapshot, deploy-release ]
+ if: ${{ always() && (needs.deploy-snapshot.result == 'success' || needs.deploy-release.result == 'success') }}
+ uses: apache/logging-parent/.github/workflows/verify-reproducibility-reusable.yaml@rel/12.0.0
+ with:
+ nexus-url: ${{ needs.deploy-release.result == 'success' && needs.deploy-release.outputs.nexus-url || needs.deploy-snapshot.outputs.nexus-url }}
+ # Checkout the repository by branch name, since the deployment job might add commits to the branch
+ ref: ${{ github.ref_name }}
+ # Encode the `runs-on` input as JSON array
+ runs-on: '["ubuntu-latest", "macos-latest", "windows-latest"]'
From 38466320b5600967e659243dc213e229903fdff5 Mon Sep 17 00:00:00 2001
From: "Piotr P. Karwasz"
Date: Tue, 18 Feb 2025 22:38:36 +0100
Subject: [PATCH 09/42] Run integration tests after each deployment (#3105)
This PR starts an `integration-test` job, whenever a snapshot or release is deployed.
---
.github/workflows/build.yaml | 15 +++++++++++++--
pom.xml | 2 +-
2 files changed, 14 insertions(+), 3 deletions(-)
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index 721a7f8a375..ddc88be30d9 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -77,10 +77,21 @@ jobs:
verify-reproducibility:
needs: [ deploy-snapshot, deploy-release ]
if: ${{ always() && (needs.deploy-snapshot.result == 'success' || needs.deploy-release.result == 'success') }}
+ name: "verify-reproducibility (${{ needs.deploy-release.result == 'success' && needs.deploy-release.outputs.project-version || needs.deploy-snapshot.outputs.project-version }})"
uses: apache/logging-parent/.github/workflows/verify-reproducibility-reusable.yaml@rel/12.0.0
with:
nexus-url: ${{ needs.deploy-release.result == 'success' && needs.deploy-release.outputs.nexus-url || needs.deploy-snapshot.outputs.nexus-url }}
- # Checkout the repository by branch name, since the deployment job might add commits to the branch
- ref: ${{ github.ref_name }}
# Encode the `runs-on` input as JSON array
runs-on: '["ubuntu-latest", "macos-latest", "windows-latest"]'
+
+ # Run integration-tests automatically after a snapshot or RC is published
+ integration-test:
+ needs: [ deploy-snapshot, deploy-release ]
+ if: ${{ always() && (needs.deploy-snapshot.result == 'success' || needs.deploy-release.result == 'success') }}
+ name: "integration-test (${{ needs.deploy-release.result == 'success' && needs.deploy-release.outputs.project-version || needs.deploy-snapshot.outputs.project-version }})"
+ uses: apache/logging-log4j-samples/.github/workflows/integration-test.yaml@main
+ with:
+ log4j-version: ${{ needs.deploy-release.result == 'success' && needs.deploy-release.outputs.project-version || needs.deploy-snapshot.outputs.project-version }}
+ log4j-repository-url: ${{ needs.deploy-release.result == 'success' && needs.deploy-release.outputs.nexus-url || needs.deploy-snapshot.outputs.nexus-url }}
+ # Use the `main` branch of `logging-log4j-samples`
+ samples-ref: 'refs/heads/main'
diff --git a/pom.xml b/pom.xml
index 177b0531d2c..31f27d334be 100644
--- a/pom.xml
+++ b/pom.xml
@@ -307,7 +307,7 @@
- 2.25.0-SNAPSHOT
+ 2.25.0.pr3105-SNAPSHOT
2.24.3
2.24.3
From 2b9a15f6eb9dda568f4c25977da71b23c72ada6c Mon Sep 17 00:00:00 2001
From: "Piotr P. Karwasz"
Date: Tue, 18 Feb 2025 23:05:34 +0100
Subject: [PATCH 10/42] Fix revision to `2.25.0-SNAPSHOT`
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index 31f27d334be..177b0531d2c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -307,7 +307,7 @@
- 2.25.0.pr3105-SNAPSHOT
+ 2.25.0-SNAPSHOT
2.24.3
2.24.3
From f203d86c1a0f06ec7b049d8e67727b05bd4d40a8 Mon Sep 17 00:00:00 2001
From: "Piotr P. Karwasz"
Date: Tue, 18 Feb 2025 23:55:11 +0100
Subject: [PATCH 11/42] Fix Nexus URL for snapshots
---
.github/workflows/build.yaml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index ddc88be30d9..3e5377d03c1 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -80,7 +80,7 @@ jobs:
name: "verify-reproducibility (${{ needs.deploy-release.result == 'success' && needs.deploy-release.outputs.project-version || needs.deploy-snapshot.outputs.project-version }})"
uses: apache/logging-parent/.github/workflows/verify-reproducibility-reusable.yaml@rel/12.0.0
with:
- nexus-url: ${{ needs.deploy-release.result == 'success' && needs.deploy-release.outputs.nexus-url || needs.deploy-snapshot.outputs.nexus-url }}
+ nexus-url: ${{ needs.deploy-release.result == 'success' && needs.deploy-release.outputs.nexus-url || 'https://repository.apache.org/content/groups/snapshots' }}
# Encode the `runs-on` input as JSON array
runs-on: '["ubuntu-latest", "macos-latest", "windows-latest"]'
From bd4607ce21604dea11326a59a3d24985f4a3b202 Mon Sep 17 00:00:00 2001
From: ASF Logging Services RM
Date: Tue, 18 Feb 2025 23:11:28 +0000
Subject: [PATCH 12/42] Update `org.apache.cassandra:cassandra-all` to version
`3.11.19` (#3440)
---
log4j-parent/pom.xml | 2 +-
.../.2.x.x/update_org_apache_cassandra_cassandra_all.xml | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/log4j-parent/pom.xml b/log4j-parent/pom.xml
index d72e426442c..1c6846a594e 100644
--- a/log4j-parent/pom.xml
+++ b/log4j-parent/pom.xml
@@ -67,7 +67,7 @@
4.2.2
2.0b6
7.1.0
- 3.11.18
+ 3.11.19
3.11.5
1.17.1
1.27.1
diff --git a/src/changelog/.2.x.x/update_org_apache_cassandra_cassandra_all.xml b/src/changelog/.2.x.x/update_org_apache_cassandra_cassandra_all.xml
index 01c4737997c..40a9dcfbec6 100644
--- a/src/changelog/.2.x.x/update_org_apache_cassandra_cassandra_all.xml
+++ b/src/changelog/.2.x.x/update_org_apache_cassandra_cassandra_all.xml
@@ -3,6 +3,6 @@
xmlns="https://logging.apache.org/xml/ns"
xsi:schemaLocation="https://logging.apache.org/xml/ns https://logging.apache.org/xml/ns/log4j-changelog-0.xsd"
type="updated">
-
- Update `org.apache.cassandra:cassandra-all` to version `3.11.18`
+
+ Update `org.apache.cassandra:cassandra-all` to version `3.11.19`
From 92d6efb089740b8302ce025650d49e12656b8ec4 Mon Sep 17 00:00:00 2001
From: "Piotr P. Karwasz"
Date: Wed, 19 Feb 2025 07:49:29 +0100
Subject: [PATCH 13/42] Activate `bom` profile in `log4j-bom`
Adds a `.logging-parent-bom-activator` file to activate the `bom`
profile.
---
.logging-parent-bom-activator | 16 ++++++++++++++++
pom.xml | 17 -----------------
2 files changed, 16 insertions(+), 17 deletions(-)
create mode 100644 .logging-parent-bom-activator
diff --git a/.logging-parent-bom-activator b/.logging-parent-bom-activator
new file mode 100644
index 00000000000..e7980a6d13b
--- /dev/null
+++ b/.logging-parent-bom-activator
@@ -0,0 +1,16 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+This file activates the `flatten-bom` profile.
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 177b0531d2c..bb67727f5fe 100644
--- a/pom.xml
+++ b/pom.xml
@@ -563,23 +563,6 @@
-
-
- org.codehaus.mojo
- flatten-maven-plugin
- ${flatten-maven-plugin.version}
-
-
- flatten-bom
-
- flatten
-
- process-resources
- false
-
-
-
-
From 2202b5847213552b81625d020c527e631eb684fc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?=
Date: Wed, 19 Feb 2025 11:19:42 +0100
Subject: [PATCH 14/42] Add Nexus URL argument to `generate-email.sh` per
`logging-parent` upgrade
---
.github/generate-email.sh | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/.github/generate-email.sh b/.github/generate-email.sh
index e825293af6d..469023ad819 100755
--- a/.github/generate-email.sh
+++ b/.github/generate-email.sh
@@ -28,12 +28,12 @@ stderr() {
fail_for_invalid_args() {
stderr "Invalid arguments!"
- stderr "Expected arguments: "
+ stderr "Expected arguments: "
exit 1
}
# Check arguments
-[ $# -ne 3 ] && fail_for_invalid_args
+[ $# -ne 4 ] && fail_for_invalid_args
# Constants
PROJECT_NAME="Apache Log4j"
@@ -43,6 +43,7 @@ PROJECT_SITE="https://logging.apache.org/$PROJECT_ID"
PROJECT_STAGING_SITE="${PROJECT_SITE/apache.org/staged.apache.org}"
PROJECT_REPO="https://github.com/apache/logging-log4j2"
COMMIT_ID="$3"
+NEXUS_URL="$4"
PROJECT_DIST_URL="https://dist.apache.org/repos/dist/dev/logging/$PROJECT_ID/$PROJECT_VERSION"
# Check release notes file
@@ -71,7 +72,7 @@ Website: $PROJECT_STAGING_SITE/$PROJECT_VERSION/index.html
GitHub: $PROJECT_REPO
Commit: $COMMIT_ID
Distribution: $PROJECT_DIST_URL
-Nexus: https://repository.apache.org/content/repositories/orgapachelogging-
+Nexus: $NEXUS_URL
Signing key: 0x077e8893a6dcc33dd4a4d5b256e73ba9a0b592d0
Review kit: https://logging.apache.org/logging-parent/release-review-instructions.html
From c3fa9462cd91fcfd6a66747213432df68ff18933 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?=
Date: Wed, 19 Feb 2025 11:23:57 +0100
Subject: [PATCH 15/42] Document `maven-compiler-plugin` override
---
log4j-parent/pom.xml | 3 +++
1 file changed, 3 insertions(+)
diff --git a/log4j-parent/pom.xml b/log4j-parent/pom.xml
index 1c6846a594e..7b7ddeb8766 100644
--- a/log4j-parent/pom.xml
+++ b/log4j-parent/pom.xml
@@ -991,6 +991,9 @@
+
org.apache.maven.plugins
maven-compiler-plugin
From ae77c09f923de98bdfc3f92e15c7c55baa34fab6 Mon Sep 17 00:00:00 2001
From: ASF Logging Services RM
Date: Wed, 19 Feb 2025 11:47:02 +0000
Subject: [PATCH 16/42] Update `org.mongodb:bson` to version `5.3.1` (#3409)
---
log4j-mongodb/pom.xml | 2 +-
src/changelog/.2.x.x/update_org_mongodb_bson.xml | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/log4j-mongodb/pom.xml b/log4j-mongodb/pom.xml
index 4991ab5ac83..822f4d382db 100644
--- a/log4j-mongodb/pom.xml
+++ b/log4j-mongodb/pom.xml
@@ -30,7 +30,7 @@
org.apache.logging.log4j.core
- 5.2.1
+ 5.3.1
2.0.16
diff --git a/src/changelog/.2.x.x/update_org_mongodb_bson.xml b/src/changelog/.2.x.x/update_org_mongodb_bson.xml
index 02f463185e0..ce86df17157 100644
--- a/src/changelog/.2.x.x/update_org_mongodb_bson.xml
+++ b/src/changelog/.2.x.x/update_org_mongodb_bson.xml
@@ -3,6 +3,6 @@
xmlns="https://logging.apache.org/xml/ns"
xsi:schemaLocation="https://logging.apache.org/xml/ns https://logging.apache.org/xml/ns/log4j-changelog-0.xsd"
type="updated">
-
- Update `org.mongodb:bson` to version `4.11.5`
+
+ Update `org.mongodb:bson` to version `5.3.1`
From 0891d6ba5f01bca8472937fd47ff87f74169b8ac Mon Sep 17 00:00:00 2001
From: "Piotr P. Karwasz"
Date: Wed, 19 Feb 2025 16:35:26 +0100
Subject: [PATCH 17/42] Fix formatting of `s` pattern (#3469)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Fixes the formatting of the single `s` pattern.
Co-authored-by: Volkan Yazıcı
---
.../InstantPatternDynamicFormatterTest.java | 29 ++++----
.../InstantPatternDynamicFormatter.java | 66 +++++++++++--------
2 files changed, 58 insertions(+), 37 deletions(-)
diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/util/internal/instant/InstantPatternDynamicFormatterTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/util/internal/instant/InstantPatternDynamicFormatterTest.java
index 7829d234aaa..307511f5bd4 100644
--- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/util/internal/instant/InstantPatternDynamicFormatterTest.java
+++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/util/internal/instant/InstantPatternDynamicFormatterTest.java
@@ -65,13 +65,13 @@ static List sequencingTestCases() {
testCases.add(Arguments.of(
"yyyyMMddHHmmssSSSX",
ChronoUnit.HOURS,
- asList(pDyn("yyyyMMddHH", ChronoUnit.HOURS), pDyn("mm"), pSec("", 3), pDyn("X"))));
+ asList(pDyn("yyyyMMddHH", ChronoUnit.HOURS), pDyn("mm"), pSec(2, "", 3), pDyn("X"))));
// `yyyyMMddHHmmssSSSX` instant cache updated per minute
testCases.add(Arguments.of(
"yyyyMMddHHmmssSSSX",
ChronoUnit.MINUTES,
- asList(pDyn("yyyyMMddHHmm", ChronoUnit.MINUTES), pSec("", 3), pDyn("X"))));
+ asList(pDyn("yyyyMMddHHmm", ChronoUnit.MINUTES), pSec(2, "", 3), pDyn("X"))));
// ISO9601 instant cache updated daily
final String iso8601InstantPattern = "yyyy-MM-dd'T'HH:mm:ss.SSSX";
@@ -81,24 +81,29 @@ static List sequencingTestCases() {
asList(
pDyn("yyyy'-'MM'-'dd'T'", ChronoUnit.DAYS),
pDyn("HH':'mm':'", ChronoUnit.MINUTES),
- pSec(".", 3),
+ pSec(2, ".", 3),
pDyn("X"))));
// ISO9601 instant cache updated per minute
testCases.add(Arguments.of(
iso8601InstantPattern,
ChronoUnit.MINUTES,
- asList(pDyn("yyyy'-'MM'-'dd'T'HH':'mm':'", ChronoUnit.MINUTES), pSec(".", 3), pDyn("X"))));
+ asList(pDyn("yyyy'-'MM'-'dd'T'HH':'mm':'", ChronoUnit.MINUTES), pSec(2, ".", 3), pDyn("X"))));
// ISO9601 instant cache updated per second
testCases.add(Arguments.of(
iso8601InstantPattern,
ChronoUnit.SECONDS,
- asList(pDyn("yyyy'-'MM'-'dd'T'HH':'mm':'", ChronoUnit.MINUTES), pSec(".", 3), pDyn("X"))));
+ asList(pDyn("yyyy'-'MM'-'dd'T'HH':'mm':'", ChronoUnit.MINUTES), pSec(2, ".", 3), pDyn("X"))));
// Seconds and micros
testCases.add(Arguments.of(
- "HH:mm:ss.SSSSSS", ChronoUnit.MINUTES, asList(pDyn("HH':'mm':'", ChronoUnit.MINUTES), pSec(".", 6))));
+ "HH:mm:ss.SSSSSS",
+ ChronoUnit.MINUTES,
+ asList(pDyn("HH':'mm':'", ChronoUnit.MINUTES), pSec(2, ".", 6))));
+
+ // Seconds without padding
+ testCases.add(Arguments.of("s.SSS", ChronoUnit.SECONDS, singletonList(pSec(1, ".", 3))));
return testCases;
}
@@ -111,12 +116,12 @@ private static DynamicPatternSequence pDyn(final String pattern, final ChronoUni
return new DynamicPatternSequence(pattern, precision);
}
- private static SecondPatternSequence pSec(String separator, int fractionalDigits) {
- return new SecondPatternSequence(true, separator, fractionalDigits);
+ private static SecondPatternSequence pSec(int secondDigits, String separator, int fractionalDigits) {
+ return new SecondPatternSequence(secondDigits, separator, fractionalDigits);
}
private static SecondPatternSequence pMilliSec() {
- return new SecondPatternSequence(false, "", 3);
+ return new SecondPatternSequence(0, "", 3);
}
@ParameterizedTest
@@ -352,7 +357,9 @@ static Stream formatterInputs() {
"yyyy-MM-dd'T'HH:mm:ss.SSS",
"yyyy-MM-dd'T'HH:mm:ss.SSSSSS",
"dd/MM/yy HH:mm:ss.SSS",
- "dd/MM/yyyy HH:mm:ss.SSS")
+ "dd/MM/yyyy HH:mm:ss.SSS",
+ // seconds without padding
+ "s.SSS")
.flatMap(InstantPatternDynamicFormatterTest::formatterInputs);
}
@@ -364,7 +371,7 @@ static Stream formatterInputs() {
Arrays.stream(TimeZone.getAvailableIDs()).map(TimeZone::getTimeZone).toArray(TimeZone[]::new);
static Stream formatterInputs(final String pattern) {
- return IntStream.range(0, 500).mapToObj(ignoredIndex -> {
+ return IntStream.range(0, 100).mapToObj(ignoredIndex -> {
final Locale locale = LOCALES[RANDOM.nextInt(LOCALES.length)];
final TimeZone timeZone = TIME_ZONES[RANDOM.nextInt(TIME_ZONES.length)];
final MutableInstant instant = randomInstant();
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/internal/instant/InstantPatternDynamicFormatter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/internal/instant/InstantPatternDynamicFormatter.java
index bb8059329ea..1c9dfab5711 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/internal/instant/InstantPatternDynamicFormatter.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/internal/instant/InstantPatternDynamicFormatter.java
@@ -239,10 +239,10 @@ private static List sequencePattern(final String pattern) {
final PatternSequence sequence;
switch (c) {
case 's':
- sequence = new SecondPatternSequence(true, "", 0);
+ sequence = new SecondPatternSequence(sequenceContent.length(), "", 0);
break;
case 'S':
- sequence = new SecondPatternSequence(false, "", sequenceContent.length());
+ sequence = new SecondPatternSequence(0, "", sequenceContent.length());
break;
default:
sequence = new DynamicPatternSequence(sequenceContent);
@@ -694,39 +694,50 @@ static class SecondPatternSequence extends PatternSequence {
100_000_000, 10_000_000, 1_000_000, 100_000, 10_000, 1_000, 100, 10, 1
};
- private final boolean printSeconds;
+ private final int secondDigits;
private final String separator;
private final int fractionalDigits;
- SecondPatternSequence(boolean printSeconds, String separator, int fractionalDigits) {
+ SecondPatternSequence(int secondDigits, String separator, int fractionalDigits) {
super(
- createPattern(printSeconds, separator, fractionalDigits),
- determinePrecision(printSeconds, fractionalDigits));
- this.printSeconds = printSeconds;
+ createPattern(secondDigits, separator, fractionalDigits),
+ determinePrecision(secondDigits, fractionalDigits));
+ final int maxSecondDigits = 2;
+ if (secondDigits > maxSecondDigits) {
+ final String message = String.format(
+ "More than %d `s` pattern letters are not supported, found: %d", maxSecondDigits, secondDigits);
+ throw new IllegalArgumentException(message);
+ }
+ final int maxFractionalDigits = 9;
+ if (fractionalDigits > maxFractionalDigits) {
+ final String message = String.format(
+ "More than %d `S` pattern letters are not supported, found: %d",
+ maxFractionalDigits, fractionalDigits);
+ throw new IllegalArgumentException(message);
+ }
+ this.secondDigits = secondDigits;
this.separator = separator;
this.fractionalDigits = fractionalDigits;
}
- private static String createPattern(boolean printSeconds, String separator, int fractionalDigits) {
- StringBuilder builder = new StringBuilder();
- if (printSeconds) {
- builder.append("ss");
- }
- builder.append(StaticPatternSequence.escapeLiteral(separator));
- if (fractionalDigits > 0) {
- builder.append(Strings.repeat("S", fractionalDigits));
- }
- return builder.toString();
+ private static String createPattern(int secondDigits, String separator, int fractionalDigits) {
+ return Strings.repeat("s", secondDigits)
+ + StaticPatternSequence.escapeLiteral(separator)
+ + Strings.repeat("S", fractionalDigits);
}
- private static ChronoUnit determinePrecision(boolean printSeconds, int digits) {
+ private static ChronoUnit determinePrecision(int secondDigits, int digits) {
if (digits > 6) return ChronoUnit.NANOS;
if (digits > 3) return ChronoUnit.MICROS;
if (digits > 0) return ChronoUnit.MILLIS;
- return printSeconds ? ChronoUnit.SECONDS : ChronoUnit.FOREVER;
+ return secondDigits > 0 ? ChronoUnit.SECONDS : ChronoUnit.FOREVER;
+ }
+
+ private static void formatUnpaddedSeconds(StringBuilder buffer, Instant instant) {
+ buffer.append(instant.getEpochSecond() % 60L);
}
- private static void formatSeconds(StringBuilder buffer, Instant instant) {
+ private static void formatPaddedSeconds(StringBuilder buffer, Instant instant) {
long secondsInMinute = instant.getEpochSecond() % 60L;
buffer.append((char) ((secondsInMinute / 10L) + '0'));
buffer.append((char) ((secondsInMinute % 10L) + '0'));
@@ -757,9 +768,12 @@ private static void formatMillis(StringBuilder buffer, Instant instant) {
@Override
InstantPatternFormatter createFormatter(Locale locale, TimeZone timeZone) {
+ final BiConsumer secondDigitsFormatter = secondDigits == 2
+ ? SecondPatternSequence::formatPaddedSeconds
+ : SecondPatternSequence::formatUnpaddedSeconds;
final BiConsumer fractionDigitsFormatter =
fractionalDigits == 3 ? SecondPatternSequence::formatMillis : this::formatFractionalDigits;
- if (!printSeconds) {
+ if (secondDigits == 0) {
return new AbstractFormatter(pattern, locale, timeZone, precision) {
@Override
public void formatTo(StringBuilder buffer, Instant instant) {
@@ -772,7 +786,7 @@ public void formatTo(StringBuilder buffer, Instant instant) {
return new AbstractFormatter(pattern, locale, timeZone, precision) {
@Override
public void formatTo(StringBuilder buffer, Instant instant) {
- formatSeconds(buffer, instant);
+ secondDigitsFormatter.accept(buffer, instant);
buffer.append(separator);
}
};
@@ -780,7 +794,7 @@ public void formatTo(StringBuilder buffer, Instant instant) {
return new AbstractFormatter(pattern, locale, timeZone, precision) {
@Override
public void formatTo(StringBuilder buffer, Instant instant) {
- formatSeconds(buffer, instant);
+ secondDigitsFormatter.accept(buffer, instant);
buffer.append(separator);
fractionDigitsFormatter.accept(buffer, instant);
}
@@ -795,15 +809,15 @@ PatternSequence tryMerge(PatternSequence other, ChronoUnit thresholdPrecision) {
StaticPatternSequence staticOther = (StaticPatternSequence) other;
if (fractionalDigits == 0) {
return new SecondPatternSequence(
- printSeconds, this.separator + staticOther.literal, fractionalDigits);
+ this.secondDigits, this.separator + staticOther.literal, fractionalDigits);
}
}
// We can always append more fractional digits
if (other instanceof SecondPatternSequence) {
SecondPatternSequence secondOther = (SecondPatternSequence) other;
- if (!secondOther.printSeconds && secondOther.separator.isEmpty()) {
+ if (secondOther.secondDigits == 0 && secondOther.separator.isEmpty()) {
return new SecondPatternSequence(
- printSeconds, this.separator, this.fractionalDigits + secondOther.fractionalDigits);
+ this.secondDigits, this.separator, this.fractionalDigits + secondOther.fractionalDigits);
}
}
return null;
From eefffd90f61950e5e75d9278879ea7454fbb150e Mon Sep 17 00:00:00 2001
From: ASF Logging Services RM
Date: Wed, 19 Feb 2025 16:03:13 +0000
Subject: [PATCH 18/42] Update `co.elastic.clients:elasticsearch-java` to
version `8.17.2` (#3460)
---
log4j-layout-template-json-test/pom.xml | 2 +-
.../.2.x.x/update_co_elastic_clients_elasticsearch_java.xml | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/log4j-layout-template-json-test/pom.xml b/log4j-layout-template-json-test/pom.xml
index 0116cd7561c..6182764d1c9 100644
--- a/log4j-layout-template-json-test/pom.xml
+++ b/log4j-layout-template-json-test/pom.xml
@@ -48,7 +48,7 @@
2. The Docker image version of the ELK-stack
As of 2024-09-16, these all (Maven artifacts and Elastic products) get released with the same version. -->
- 8.17.0
+ 8.17.2
diff --git a/src/changelog/.2.x.x/update_co_elastic_clients_elasticsearch_java.xml b/src/changelog/.2.x.x/update_co_elastic_clients_elasticsearch_java.xml
index 85ee84ee0c4..d2312d6073d 100644
--- a/src/changelog/.2.x.x/update_co_elastic_clients_elasticsearch_java.xml
+++ b/src/changelog/.2.x.x/update_co_elastic_clients_elasticsearch_java.xml
@@ -3,6 +3,6 @@
xmlns="https://logging.apache.org/xml/ns"
xsi:schemaLocation="https://logging.apache.org/xml/ns https://logging.apache.org/xml/ns/log4j-changelog-0.xsd"
type="updated">
-
- Update `co.elastic.clients:elasticsearch-java` to version `8.17.0`
+
+ Update `co.elastic.clients:elasticsearch-java` to version `8.17.2`
From 71a03d754214e3a3b858b3e65bc96c953b76dd1f Mon Sep 17 00:00:00 2001
From: ASF Logging Services RM
Date: Wed, 19 Feb 2025 20:01:35 +0000
Subject: [PATCH 19/42] Update `commons-codec:commons-codec` to version
`1.18.0` (#3421)
---
log4j-parent/pom.xml | 2 +-
.../.2.x.x/update_commons_codec_commons_codec.xml | 8 ++++++++
2 files changed, 9 insertions(+), 1 deletion(-)
create mode 100644 src/changelog/.2.x.x/update_commons_codec_commons_codec.xml
diff --git a/log4j-parent/pom.xml b/log4j-parent/pom.xml
index 7b7ddeb8766..771e42dc118 100644
--- a/log4j-parent/pom.xml
+++ b/log4j-parent/pom.xml
@@ -69,7 +69,7 @@
7.1.0
3.11.19
3.11.5
- 1.17.1
+ 1.18.0
1.27.1
1.13.0
2.13.0
diff --git a/src/changelog/.2.x.x/update_commons_codec_commons_codec.xml b/src/changelog/.2.x.x/update_commons_codec_commons_codec.xml
new file mode 100644
index 00000000000..dd89663c0b9
--- /dev/null
+++ b/src/changelog/.2.x.x/update_commons_codec_commons_codec.xml
@@ -0,0 +1,8 @@
+
+
+
+ Update `commons-codec:commons-codec` to version `1.18.0`
+
From f98bff4d662f6c4d9893af197188e1391ec5e74d Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 19 Feb 2025 23:56:54 +0100
Subject: [PATCH 20/42] Bump commons-logging:commons-logging in /log4j-parent
(#3445)
Bumps commons-logging:commons-logging from 1.3.4 to 1.3.5.
---
updated-dependencies:
- dependency-name: commons-logging:commons-logging
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
log4j-parent/pom.xml | 2 +-
pom.xml | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/log4j-parent/pom.xml b/log4j-parent/pom.xml
index 771e42dc118..8a8d9de2397 100644
--- a/log4j-parent/pom.xml
+++ b/log4j-parent/pom.xml
@@ -75,7 +75,7 @@
2.13.0
2.18.0
3.17.0
- 1.3.4
+ 1.3.5
1.2.15
3.4.4
diff --git a/pom.xml b/pom.xml
index bb67727f5fe..381e9aec3c1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -341,7 +341,7 @@
true
1.27.1
1.13.0
- 1.3.4
+ 1.3.5
1.2.21
4.0.0
1.11.0
From 5aac7d6ce9f077fd0006cd8975eac5baf32187a6 Mon Sep 17 00:00:00 2001
From: ASF Logging Services RM
Date: Wed, 19 Feb 2025 23:25:17 +0000
Subject: [PATCH 21/42] Update
`org.openrewrite.recipe:rewrite-logging-frameworks` to version `3.2.0`
(#3446)
---
pom.xml | 2 +-
...date_org_openrewrite_recipe_rewrite_logging_frameworks.xml | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/pom.xml b/pom.xml
index 381e9aec3c1..1fb7338f7ca 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1059,7 +1059,7 @@
org.openrewrite.recipe
rewrite-logging-frameworks
- 2.3.0
+ 3.2.0
diff --git a/src/changelog/.2.x.x/update_org_openrewrite_recipe_rewrite_logging_frameworks.xml b/src/changelog/.2.x.x/update_org_openrewrite_recipe_rewrite_logging_frameworks.xml
index 7107b37660a..910698cf1e6 100644
--- a/src/changelog/.2.x.x/update_org_openrewrite_recipe_rewrite_logging_frameworks.xml
+++ b/src/changelog/.2.x.x/update_org_openrewrite_recipe_rewrite_logging_frameworks.xml
@@ -3,6 +3,6 @@
xmlns="https://logging.apache.org/xml/ns"
xsi:schemaLocation="https://logging.apache.org/xml/ns https://logging.apache.org/xml/ns/log4j-changelog-0.xsd"
type="updated">
-
- Update `org.openrewrite.recipe:rewrite-logging-frameworks` to version `2.3.0`
+
+ Update `org.openrewrite.recipe:rewrite-logging-frameworks` to version `3.2.0`
From 07590bc087204ccd4d091c65d0c5da255c92df68 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?=
Date: Thu, 20 Feb 2025 18:07:55 +0100
Subject: [PATCH 22/42] Add `collectionName` and `databaseName` attributes to
`MongoDbProvider` (#3467)
Co-authored-by: Josh Smith
---
.../mongodb/MongoDbCollectionNameIT.java | 36 ++++++
.../MongoDbDatabaseAndCollectionNameIT.java | 36 ++++++
.../MongoDbNoDatabaseAndCollectionNameIT.java | 36 ++++++
.../resources/MongoDbCollectionNameIT.xml | 37 ++++++
.../MongoDbDatabaseAndCollectionNameIT.xml | 39 ++++++
.../MongoDbNoDatabaseAndCollectionNameIT.xml | 37 ++++++
log4j-mongodb4/pom.xml | 15 ---
.../log4j/mongodb4/MongoDb4Connection.java | 30 ++++-
.../log4j/mongodb4/MongoDb4Provider.java | 110 +++++++++++++----
.../mongodb4/AbstractMongoDb4CappedIT.java | 6 +-
.../mongodb4/MongoDb4CollectionNameIT.java | 36 ++++++
.../MongoDb4DatabaseAndCollectionNameIT.java | 36 ++++++
.../log4j/mongodb4/MongoDb4ProviderTest.java | 116 ++++++++++++++++++
.../log4j/mongodb4/MongoDb4Resolver.java | 2 -
.../resources/MongoDb4CollectionNameIT.xml | 36 ++++++
.../MongoDb4DatabaseAndCollectionNameIT.xml | 38 ++++++
.../.2.x.x/3467_add_mongodb_conn_db_name.xml | 8 ++
.../appenders/database/nosql-mongo-keys.json | 4 +-
.../database/nosql-mongo-keys.properties | 4 +-
.../appenders/database/nosql-mongo-keys.xml | 4 +-
.../appenders/database/nosql-mongo-keys.yaml | 4 +-
.../appenders/database/nosql-mongo.json | 4 +-
.../appenders/database/nosql-mongo.properties | 4 +-
.../manual/appenders/database/nosql-mongo.xml | 4 +-
.../appenders/database/nosql-mongo.yaml | 4 +-
.../ROOT/pages/manual/appenders/database.adoc | 32 +++--
26 files changed, 655 insertions(+), 63 deletions(-)
create mode 100644 log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbCollectionNameIT.java
create mode 100644 log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbDatabaseAndCollectionNameIT.java
create mode 100644 log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbNoDatabaseAndCollectionNameIT.java
create mode 100644 log4j-mongodb/src/test/resources/MongoDbCollectionNameIT.xml
create mode 100644 log4j-mongodb/src/test/resources/MongoDbDatabaseAndCollectionNameIT.xml
create mode 100644 log4j-mongodb/src/test/resources/MongoDbNoDatabaseAndCollectionNameIT.xml
create mode 100644 log4j-mongodb4/src/test/java/org/apache/logging/log4j/mongodb4/MongoDb4CollectionNameIT.java
create mode 100644 log4j-mongodb4/src/test/java/org/apache/logging/log4j/mongodb4/MongoDb4DatabaseAndCollectionNameIT.java
create mode 100644 log4j-mongodb4/src/test/java/org/apache/logging/log4j/mongodb4/MongoDb4ProviderTest.java
create mode 100644 log4j-mongodb4/src/test/resources/MongoDb4CollectionNameIT.xml
create mode 100644 log4j-mongodb4/src/test/resources/MongoDb4DatabaseAndCollectionNameIT.xml
create mode 100644 src/changelog/.2.x.x/3467_add_mongodb_conn_db_name.xml
diff --git a/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbCollectionNameIT.java b/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbCollectionNameIT.java
new file mode 100644
index 00000000000..60c74dff597
--- /dev/null
+++ b/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbCollectionNameIT.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.logging.log4j.mongodb;
+
+import com.mongodb.client.MongoClient;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.test.junit.LoggerContextSource;
+import org.apache.logging.log4j.test.junit.UsingStatusListener;
+import org.junit.jupiter.api.Test;
+
+@UsingMongoDb
+@LoggerContextSource("MongoDbCollectionNameIT.xml")
+// Print debug status logger output upon failure
+@UsingStatusListener
+class MongoDbCollectionNameIT extends AbstractMongoDbCappedIT {
+
+ @Test
+ @Override
+ protected void test(LoggerContext ctx, MongoClient mongoClient) {
+ super.test(ctx, mongoClient);
+ }
+}
diff --git a/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbDatabaseAndCollectionNameIT.java b/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbDatabaseAndCollectionNameIT.java
new file mode 100644
index 00000000000..8d399d10ac9
--- /dev/null
+++ b/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbDatabaseAndCollectionNameIT.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.logging.log4j.mongodb;
+
+import com.mongodb.client.MongoClient;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.test.junit.LoggerContextSource;
+import org.apache.logging.log4j.test.junit.UsingStatusListener;
+import org.junit.jupiter.api.Test;
+
+@UsingMongoDb
+@LoggerContextSource("MongoDbDatabaseAndCollectionNameIT.xml")
+// Print debug status logger output upon failure
+@UsingStatusListener
+class MongoDbDatabaseAndCollectionNameIT extends AbstractMongoDbCappedIT {
+
+ @Test
+ @Override
+ protected void test(LoggerContext ctx, MongoClient mongoClient) {
+ super.test(ctx, mongoClient);
+ }
+}
diff --git a/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbNoDatabaseAndCollectionNameIT.java b/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbNoDatabaseAndCollectionNameIT.java
new file mode 100644
index 00000000000..ba6a654fdb9
--- /dev/null
+++ b/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbNoDatabaseAndCollectionNameIT.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.logging.log4j.mongodb;
+
+import com.mongodb.client.MongoClient;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.test.junit.LoggerContextSource;
+import org.apache.logging.log4j.test.junit.UsingStatusListener;
+import org.junit.jupiter.api.Test;
+
+@UsingMongoDb
+@LoggerContextSource("MongoDbNoDatabaseAndCollectionNameIT.xml")
+// Print debug status logger output upon failure
+@UsingStatusListener
+class MongoDbNoDatabaseAndCollectionNameIT extends AbstractMongoDbCappedIT {
+
+ @Test
+ @Override
+ protected void test(LoggerContext ctx, MongoClient mongoClient) {
+ super.test(ctx, mongoClient);
+ }
+}
diff --git a/log4j-mongodb/src/test/resources/MongoDbCollectionNameIT.xml b/log4j-mongodb/src/test/resources/MongoDbCollectionNameIT.xml
new file mode 100644
index 00000000000..4b9b53842ed
--- /dev/null
+++ b/log4j-mongodb/src/test/resources/MongoDbCollectionNameIT.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-mongodb/src/test/resources/MongoDbDatabaseAndCollectionNameIT.xml b/log4j-mongodb/src/test/resources/MongoDbDatabaseAndCollectionNameIT.xml
new file mode 100644
index 00000000000..b23b551fa21
--- /dev/null
+++ b/log4j-mongodb/src/test/resources/MongoDbDatabaseAndCollectionNameIT.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-mongodb/src/test/resources/MongoDbNoDatabaseAndCollectionNameIT.xml b/log4j-mongodb/src/test/resources/MongoDbNoDatabaseAndCollectionNameIT.xml
new file mode 100644
index 00000000000..0068ab3ee38
--- /dev/null
+++ b/log4j-mongodb/src/test/resources/MongoDbNoDatabaseAndCollectionNameIT.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-mongodb4/pom.xml b/log4j-mongodb4/pom.xml
index fc25d4ace9a..9115fe4175f 100644
--- a/log4j-mongodb4/pom.xml
+++ b/log4j-mongodb4/pom.xml
@@ -134,16 +134,6 @@
org.apache.maven.plugins
maven-surefire-plugin
-
- true
-
-
-
- org.junit.jupiter
- junit-jupiter-engine
- ${junit-jupiter.version}
-
-
@@ -236,11 +226,6 @@
**/*IT.java
-
- OFF
${mongo.port}
diff --git a/log4j-mongodb4/src/main/java/org/apache/logging/log4j/mongodb4/MongoDb4Connection.java b/log4j-mongodb4/src/main/java/org/apache/logging/log4j/mongodb4/MongoDb4Connection.java
index 1d9537978c4..dd7a3a927c1 100644
--- a/log4j-mongodb4/src/main/java/org/apache/logging/log4j/mongodb4/MongoDb4Connection.java
+++ b/log4j-mongodb4/src/main/java/org/apache/logging/log4j/mongodb4/MongoDb4Connection.java
@@ -41,7 +41,7 @@ public final class MongoDb4Connection extends AbstractNoSqlConnection getOrCreateMongoCollection(
final MongoDatabase database, final String collectionName, final boolean isCapped, final Long sizeInBytes) {
try {
- LOGGER.debug("Gettting collection '{}'...", collectionName);
+ LOGGER.debug("Getting collection '{}'...", collectionName);
// throws IllegalArgumentException if collectionName is invalid
final MongoCollection found = database.getCollection(collectionName);
LOGGER.debug("Got collection {}", found);
@@ -63,15 +63,29 @@ private static MongoCollection getOrCreateMongoCollection(
private final MongoCollection collection;
private final MongoClient mongoClient;
+ /**
+ * @deprecated Use {@link #MongoDb4Connection(ConnectionString, MongoClient, MongoDatabase, String, boolean, Long)} instead
+ */
+ @Deprecated
public MongoDb4Connection(
final ConnectionString connectionString,
final MongoClient mongoClient,
final MongoDatabase mongoDatabase,
final boolean isCapped,
final Integer sizeInBytes) {
- this(connectionString, mongoClient, mongoDatabase, isCapped, Long.valueOf(sizeInBytes));
+ this(
+ connectionString,
+ mongoClient,
+ mongoDatabase,
+ connectionString.getCollection(),
+ isCapped,
+ Long.valueOf(sizeInBytes));
}
+ /**
+ * @deprecated Use {@link #MongoDb4Connection(ConnectionString, MongoClient, MongoDatabase, String, boolean, Long)} instead
+ */
+ @Deprecated
public MongoDb4Connection(
final ConnectionString connectionString,
final MongoClient mongoClient,
@@ -84,6 +98,18 @@ public MongoDb4Connection(
getOrCreateMongoCollection(mongoDatabase, connectionString.getCollection(), isCapped, sizeInBytes);
}
+ public MongoDb4Connection(
+ final ConnectionString connectionString,
+ final MongoClient mongoClient,
+ final MongoDatabase mongoDatabase,
+ final String collectionName,
+ final boolean isCapped,
+ final Long sizeInBytes) {
+ this.connectionString = connectionString;
+ this.mongoClient = mongoClient;
+ this.collection = getOrCreateMongoCollection(mongoDatabase, collectionName, isCapped, sizeInBytes);
+ }
+
@Override
public void closeImpl() {
// LOG4J2-1196
diff --git a/log4j-mongodb4/src/main/java/org/apache/logging/log4j/mongodb4/MongoDb4Provider.java b/log4j-mongodb4/src/main/java/org/apache/logging/log4j/mongodb4/MongoDb4Provider.java
index 99e392237b4..18cf93f2356 100644
--- a/log4j-mongodb4/src/main/java/org/apache/logging/log4j/mongodb4/MongoDb4Provider.java
+++ b/log4j-mongodb4/src/main/java/org/apache/logging/log4j/mongodb4/MongoDb4Provider.java
@@ -18,10 +18,10 @@
import com.mongodb.ConnectionString;
import com.mongodb.MongoClientSettings;
+import com.mongodb.MongoNamespace;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoDatabase;
-import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.Core;
import org.apache.logging.log4j.core.appender.nosql.NoSqlProvider;
import org.apache.logging.log4j.core.config.plugins.Plugin;
@@ -40,6 +40,8 @@
@Plugin(name = MongoDb4Provider.PLUGIN_NAME, category = Core.CATEGORY_NAME, printObject = true)
public final class MongoDb4Provider implements NoSqlProvider {
+ private static final StatusLogger LOGGER = StatusLogger.getLogger();
+
static final String PLUGIN_NAME = "MongoDb4";
/**
@@ -60,14 +62,20 @@ public static class Builder> extends AbstractFilterable.Bui
@PluginBuilderAttribute("capped")
private boolean capped = false;
+ @PluginBuilderAttribute("collectionName")
+ private String collectionName;
+
+ @PluginBuilderAttribute("databaseName")
+ private String databaseName;
+
@Override
public MongoDb4Provider build() {
- StatusLogger.getLogger().warn("The {} Appender is deprecated, use the MongoDb Appender.", PLUGIN_NAME);
+ LOGGER.warn("The {} Appender is deprecated, use the MongoDb Appender instead.", PLUGIN_NAME);
return newMongoDb4Provider();
}
protected MongoDb4Provider newMongoDb4Provider() {
- return new MongoDb4Provider(connectionStringSource, capped, collectionSize);
+ return new MongoDb4Provider(connectionStringSource, databaseName, collectionName, capped, collectionSize);
}
/**
@@ -113,16 +121,34 @@ public B setCollectionSize(final long sizeInBytes) {
this.collectionSize = sizeInBytes;
return asBuilder();
}
- }
- private static final Logger LOGGER = StatusLogger.getLogger();
+ /**
+ * Sets name of the collection for the appender to output to
+ *
+ * @param collectionName the name of the collection for the appender to output to
+ * @return this instance.
+ */
+ public B setCollectionName(final String collectionName) {
+ this.collectionName = collectionName;
+ return asBuilder();
+ }
+
+ /**
+ * Sets the name of the logical database for the appender to output to.
+ *
+ * @param databaseName the name of the DB for the appender to output to
+ * @return this instance.
+ */
+ public B setDatabaseName(final String databaseName) {
+ this.databaseName = databaseName;
+ return asBuilder();
+ }
+ }
- // @formatter:off
private static final CodecRegistry CODEC_REGISTRIES = CodecRegistries.fromRegistries(
MongoClientSettings.getDefaultCodecRegistry(),
CodecRegistries.fromCodecs(MongoDb4LevelCodec.INSTANCE),
CodecRegistries.fromCodecs(new MongoDb4DocumentObjectCodec()));
- // @formatter:on
// TODO Where does this number come from?
private static final long DEFAULT_COLLECTION_SIZE = 536_870_912;
@@ -140,47 +166,81 @@ public static > B newBuilder() {
private final Long collectionSize;
private final boolean isCapped;
+ private final String collectionName;
private final MongoClient mongoClient;
private final MongoDatabase mongoDatabase;
private final ConnectionString connectionString;
- private MongoDb4Provider(final String connectionStringSource, final boolean isCapped, final Long collectionSize) {
- LOGGER.debug("Creating ConnectionString {}...", connectionStringSource);
- this.connectionString = new ConnectionString(connectionStringSource);
- LOGGER.debug("Created ConnectionString {}", connectionString);
- LOGGER.debug("Creating MongoClientSettings...");
- // @formatter:off
+ private MongoDb4Provider(
+ final String connectionStringSource,
+ final String databaseName,
+ final String collectionName,
+ final boolean isCapped,
+ final Long collectionSize) {
+ this.connectionString = createConnectionString(connectionStringSource);
final MongoClientSettings settings = MongoClientSettings.builder()
.applyConnectionString(this.connectionString)
.codecRegistry(CODEC_REGISTRIES)
.build();
- // @formatter:on
- LOGGER.debug("Created MongoClientSettings {}", settings);
- LOGGER.debug("Creating MongoClient {}...", settings);
this.mongoClient = MongoClients.create(settings);
- LOGGER.debug("Created MongoClient {}", mongoClient);
- final String databaseName = this.connectionString.getDatabase();
- LOGGER.debug("Getting MongoDatabase {}...", databaseName);
- this.mongoDatabase = this.mongoClient.getDatabase(databaseName);
- LOGGER.debug("Got MongoDatabase {}", mongoDatabase);
+ this.mongoDatabase = createDatabase(connectionString, databaseName, mongoClient);
this.isCapped = isCapped;
this.collectionSize = collectionSize;
+ this.collectionName = getEffectiveCollectionName(connectionString, collectionName);
+ LOGGER.debug("instantiated {}", this);
+ }
+
+ private static ConnectionString createConnectionString(final String connectionStringSource) {
+ try {
+ return new ConnectionString(connectionStringSource);
+ } catch (final IllegalArgumentException error) {
+ final String message = String.format("Invalid MongoDB connection string: `%s`", connectionStringSource);
+ throw new IllegalArgumentException(message, error);
+ }
+ }
+
+ private static MongoDatabase createDatabase(
+ final ConnectionString connectionString, final String databaseName, final MongoClient client) {
+ final String effectiveDatabaseName = databaseName != null ? databaseName : connectionString.getDatabase();
+ try {
+ // noinspection DataFlowIssue
+ MongoNamespace.checkDatabaseNameValidity(effectiveDatabaseName);
+ } catch (final IllegalArgumentException error) {
+ final String message = String.format("Invalid MongoDB database name: `%s`", effectiveDatabaseName);
+ throw new IllegalArgumentException(message, error);
+ }
+ return client.getDatabase(effectiveDatabaseName);
+ }
+
+ private static String getEffectiveCollectionName(
+ final ConnectionString connectionString, final String collectionName) {
+ final String effectiveCollectionName =
+ collectionName != null ? collectionName : connectionString.getCollection();
+ try {
+ // noinspection DataFlowIssue
+ MongoNamespace.checkCollectionNameValidity(effectiveCollectionName);
+ } catch (final IllegalArgumentException error) {
+ final String message = String.format("Invalid MongoDB collection name: `%s`", effectiveCollectionName);
+ throw new IllegalArgumentException(message, error);
+ }
+ return effectiveCollectionName;
}
@Override
public MongoDb4Connection getConnection() {
- return new MongoDb4Connection(connectionString, mongoClient, mongoDatabase, isCapped, collectionSize);
+ return new MongoDb4Connection(
+ connectionString, mongoClient, mongoDatabase, collectionName, isCapped, collectionSize);
}
@Override
public String toString() {
return String.format(
- "%s [connectionString=%s, collectionSize=%s, isCapped=%s, mongoClient=%s, mongoDatabase=%s]",
+ "%s [connectionString=`%s`, collectionSize=%s, isCapped=%s, databaseName=`%s`, collectionName=`%s`]",
MongoDb4Provider.class.getSimpleName(),
connectionString,
collectionSize,
isCapped,
- mongoClient,
- mongoDatabase);
+ mongoDatabase.getName(),
+ collectionName);
}
}
diff --git a/log4j-mongodb4/src/test/java/org/apache/logging/log4j/mongodb4/AbstractMongoDb4CappedIT.java b/log4j-mongodb4/src/test/java/org/apache/logging/log4j/mongodb4/AbstractMongoDb4CappedIT.java
index 07483dd3cf2..caac94e6b5e 100644
--- a/log4j-mongodb4/src/test/java/org/apache/logging/log4j/mongodb4/AbstractMongoDb4CappedIT.java
+++ b/log4j-mongodb4/src/test/java/org/apache/logging/log4j/mongodb4/AbstractMongoDb4CappedIT.java
@@ -24,6 +24,7 @@
import com.mongodb.client.MongoDatabase;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.status.StatusLogger;
import org.bson.Document;
abstract class AbstractMongoDb4CappedIT {
@@ -33,8 +34,9 @@ protected void test(final LoggerContext ctx, final MongoClient mongoClient) {
logger.info("Hello log");
final MongoDatabase database = mongoClient.getDatabase(MongoDb4TestConstants.DATABASE_NAME);
assertNotNull(database);
- final MongoCollection collection =
- database.getCollection(getClass().getSimpleName());
+ final String collectionName = getClass().getSimpleName();
+ StatusLogger.getLogger().debug("Using collection name: {}", collectionName);
+ final MongoCollection collection = database.getCollection(collectionName);
assertNotNull(collection);
final Document first = collection.find().first();
assertNotNull(first);
diff --git a/log4j-mongodb4/src/test/java/org/apache/logging/log4j/mongodb4/MongoDb4CollectionNameIT.java b/log4j-mongodb4/src/test/java/org/apache/logging/log4j/mongodb4/MongoDb4CollectionNameIT.java
new file mode 100644
index 00000000000..f714ee2eb82
--- /dev/null
+++ b/log4j-mongodb4/src/test/java/org/apache/logging/log4j/mongodb4/MongoDb4CollectionNameIT.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.logging.log4j.mongodb4;
+
+import com.mongodb.client.MongoClient;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.test.junit.LoggerContextSource;
+import org.apache.logging.log4j.test.junit.UsingStatusListener;
+import org.junit.jupiter.api.Test;
+
+@UsingMongoDb4
+@LoggerContextSource("MongoDb4CollectionNameIT.xml")
+// Print debug status logger output upon failure
+@UsingStatusListener
+class MongoDb4CollectionNameIT extends AbstractMongoDb4CappedIT {
+
+ @Test
+ @Override
+ protected void test(LoggerContext ctx, MongoClient mongoClient) {
+ super.test(ctx, mongoClient);
+ }
+}
diff --git a/log4j-mongodb4/src/test/java/org/apache/logging/log4j/mongodb4/MongoDb4DatabaseAndCollectionNameIT.java b/log4j-mongodb4/src/test/java/org/apache/logging/log4j/mongodb4/MongoDb4DatabaseAndCollectionNameIT.java
new file mode 100644
index 00000000000..3c06692a64a
--- /dev/null
+++ b/log4j-mongodb4/src/test/java/org/apache/logging/log4j/mongodb4/MongoDb4DatabaseAndCollectionNameIT.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.logging.log4j.mongodb4;
+
+import com.mongodb.client.MongoClient;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.test.junit.LoggerContextSource;
+import org.apache.logging.log4j.test.junit.UsingStatusListener;
+import org.junit.jupiter.api.Test;
+
+@UsingMongoDb4
+@LoggerContextSource("MongoDb4DatabaseAndCollectionNameIT.xml")
+// Print debug status logger output upon failure
+@UsingStatusListener
+class MongoDb4DatabaseAndCollectionNameIT extends AbstractMongoDb4CappedIT {
+
+ @Test
+ @Override
+ protected void test(LoggerContext ctx, MongoClient mongoClient) {
+ super.test(ctx, mongoClient);
+ }
+}
diff --git a/log4j-mongodb4/src/test/java/org/apache/logging/log4j/mongodb4/MongoDb4ProviderTest.java b/log4j-mongodb4/src/test/java/org/apache/logging/log4j/mongodb4/MongoDb4ProviderTest.java
new file mode 100644
index 00000000000..8588ead05ec
--- /dev/null
+++ b/log4j-mongodb4/src/test/java/org/apache/logging/log4j/mongodb4/MongoDb4ProviderTest.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.logging.log4j.mongodb4;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import com.mongodb.MongoNamespace;
+import com.mongodb.client.MongoCollection;
+import java.lang.reflect.Field;
+import org.bson.Document;
+import org.junit.jupiter.api.Test;
+
+class MongoDb4ProviderTest {
+
+ private static final String CON_STR_WO_DB = "mongodb://localhost:27017";
+
+ private static final String CON_STR_W_DB = "mongodb://localhost:27017/logging";
+
+ private static final String CON_STR_DB_COLL = "mongodb://localhost:27017/logging.logs";
+
+ private static final String COLLECTION_NAME = "logsTest";
+
+ private static final String DATABASE_NAME = "loggingTest";
+
+ @Test
+ void createProviderWithDatabaseAndCollectionProvidedViaConfig() {
+ MongoDb4Provider provider = MongoDb4Provider.newBuilder()
+ .setConnectionStringSource(CON_STR_WO_DB)
+ .setDatabaseName(DATABASE_NAME)
+ .setCollectionName(COLLECTION_NAME)
+ .build();
+ assertThat(provider).isNotNull();
+ assertProviderNamespace(provider, DATABASE_NAME, COLLECTION_NAME);
+ }
+
+ @Test
+ void createProviderWithoutDatabaseName() {
+ assertThatThrownBy(() -> MongoDb4Provider.newBuilder()
+ .setConnectionStringSource(CON_STR_WO_DB)
+ .build())
+ .hasMessage("Invalid MongoDB database name: `null`");
+ }
+
+ @Test
+ void createProviderWithoutDatabaseNameWithCollectionName() {
+ assertThatThrownBy(() -> MongoDb4Provider.newBuilder()
+ .setConnectionStringSource(CON_STR_WO_DB)
+ .setCollectionName(COLLECTION_NAME)
+ .build())
+ .hasMessage("Invalid MongoDB database name: `null`");
+ }
+
+ @Test
+ void createProviderWithoutCollectionName() {
+ assertThatThrownBy(() -> MongoDb4Provider.newBuilder()
+ .setConnectionStringSource(CON_STR_WO_DB)
+ .setDatabaseName(DATABASE_NAME)
+ .build())
+ .hasMessage("Invalid MongoDB collection name: `null`");
+ }
+
+ @Test
+ void createProviderWithDatabaseOnConnectionString() {
+ MongoDb4Provider provider = MongoDb4Provider.newBuilder()
+ .setConnectionStringSource(CON_STR_W_DB)
+ .setCollectionName(COLLECTION_NAME)
+ .build();
+ assertThat(provider).isNotNull();
+ assertProviderNamespace(provider, "logging", COLLECTION_NAME);
+ }
+
+ @Test
+ void createProviderConfigOverridesConnectionString() {
+ MongoDb4Provider provider = MongoDb4Provider.newBuilder()
+ .setConnectionStringSource(CON_STR_DB_COLL)
+ .setCollectionName(COLLECTION_NAME)
+ .setDatabaseName(DATABASE_NAME)
+ .build();
+ assertThat(provider).isNotNull();
+ assertProviderNamespace(provider, DATABASE_NAME, COLLECTION_NAME);
+ }
+
+ private static void assertProviderNamespace(MongoDb4Provider provider, String databaseName, String collectionName) {
+ MongoNamespace namespace = providerNamespace(provider);
+ assertThat(namespace.getDatabaseName()).isEqualTo(databaseName);
+ assertThat(namespace.getCollectionName()).isEqualTo(collectionName);
+ }
+
+ private static MongoNamespace providerNamespace(MongoDb4Provider provider) {
+ try {
+ MongoDb4Connection connection = provider.getConnection();
+ Field collectionField = MongoDb4Connection.class.getDeclaredField("collection");
+ collectionField.setAccessible(true);
+ @SuppressWarnings("unchecked")
+ MongoCollection collection = (MongoCollection) collectionField.get(connection);
+ return collection.getNamespace();
+ } catch (Exception exception) {
+ throw new RuntimeException(exception);
+ }
+ }
+}
diff --git a/log4j-mongodb4/src/test/java/org/apache/logging/log4j/mongodb4/MongoDb4Resolver.java b/log4j-mongodb4/src/test/java/org/apache/logging/log4j/mongodb4/MongoDb4Resolver.java
index 0534c09631a..c4e1628db4b 100644
--- a/log4j-mongodb4/src/test/java/org/apache/logging/log4j/mongodb4/MongoDb4Resolver.java
+++ b/log4j-mongodb4/src/test/java/org/apache/logging/log4j/mongodb4/MongoDb4Resolver.java
@@ -30,8 +30,6 @@
class MongoDb4Resolver extends TypeBasedParameterResolver implements BeforeAllCallback {
- static final String PORT_PROPERTY = "log4j2.mongo.port";
-
public MongoDb4Resolver() {
super(MongoClient.class);
}
diff --git a/log4j-mongodb4/src/test/resources/MongoDb4CollectionNameIT.xml b/log4j-mongodb4/src/test/resources/MongoDb4CollectionNameIT.xml
new file mode 100644
index 00000000000..09834dc526c
--- /dev/null
+++ b/log4j-mongodb4/src/test/resources/MongoDb4CollectionNameIT.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-mongodb4/src/test/resources/MongoDb4DatabaseAndCollectionNameIT.xml b/log4j-mongodb4/src/test/resources/MongoDb4DatabaseAndCollectionNameIT.xml
new file mode 100644
index 00000000000..790c991ccf7
--- /dev/null
+++ b/log4j-mongodb4/src/test/resources/MongoDb4DatabaseAndCollectionNameIT.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/changelog/.2.x.x/3467_add_mongodb_conn_db_name.xml b/src/changelog/.2.x.x/3467_add_mongodb_conn_db_name.xml
new file mode 100644
index 00000000000..ecf5d7e625b
--- /dev/null
+++ b/src/changelog/.2.x.x/3467_add_mongodb_conn_db_name.xml
@@ -0,0 +1,8 @@
+
+
+
+ Add `collectionName` and `databaseName` arguments to the MongoDB appender
+
diff --git a/src/site/antora/modules/ROOT/examples/manual/appenders/database/nosql-mongo-keys.json b/src/site/antora/modules/ROOT/examples/manual/appenders/database/nosql-mongo-keys.json
index 2b47690c0b5..702a4c3591c 100644
--- a/src/site/antora/modules/ROOT/examples/manual/appenders/database/nosql-mongo-keys.json
+++ b/src/site/antora/modules/ROOT/examples/manual/appenders/database/nosql-mongo-keys.json
@@ -5,7 +5,9 @@
"NoSql": {
"name": "MONGO",
"MongoDb": {
- "connection": "mongodb://${env:DB_USER}:${env:DB_PASS}@localhost:27017/logging.logs"
+ "connection": "mongodb://${env:DB_USER}:${env:DB_PASS}@localhost:27017/",
+ "databaseName": "logging",
+ "collectionName": "logs"
},
"KeyValuePair": [
{
diff --git a/src/site/antora/modules/ROOT/examples/manual/appenders/database/nosql-mongo-keys.properties b/src/site/antora/modules/ROOT/examples/manual/appenders/database/nosql-mongo-keys.properties
index e0e97ac2872..ba3112e8951 100644
--- a/src/site/antora/modules/ROOT/examples/manual/appenders/database/nosql-mongo-keys.properties
+++ b/src/site/antora/modules/ROOT/examples/manual/appenders/database/nosql-mongo-keys.properties
@@ -19,7 +19,9 @@
appender.0.type = NoSql
appender.0.name = MONGO
appender.0.provider.type = MongoDB
-appender.0.provider.connection = mongodb://${env:DB_USER}:${env:DB_PASS}@localhost:27017/logging.logs
+appender.0.provider.connection = mongodb://${env:DB_USER}:${env:DB_PASS}@localhost:27017
+appender.0.provider.databaseName = logging
+appender.0.provider.collectionName = logs
appender.0.kv[0].type = KeyValuePair
appender.0.kv[0].key = startTime
diff --git a/src/site/antora/modules/ROOT/examples/manual/appenders/database/nosql-mongo-keys.xml b/src/site/antora/modules/ROOT/examples/manual/appenders/database/nosql-mongo-keys.xml
index cd9f458ac3a..fc4e3b06972 100644
--- a/src/site/antora/modules/ROOT/examples/manual/appenders/database/nosql-mongo-keys.xml
+++ b/src/site/antora/modules/ROOT/examples/manual/appenders/database/nosql-mongo-keys.xml
@@ -23,7 +23,9 @@
-
+
diff --git a/src/site/antora/modules/ROOT/examples/manual/appenders/database/nosql-mongo-keys.yaml b/src/site/antora/modules/ROOT/examples/manual/appenders/database/nosql-mongo-keys.yaml
index 2cb5df2c85f..1b286eec629 100644
--- a/src/site/antora/modules/ROOT/examples/manual/appenders/database/nosql-mongo-keys.yaml
+++ b/src/site/antora/modules/ROOT/examples/manual/appenders/database/nosql-mongo-keys.yaml
@@ -20,7 +20,9 @@ Configuration:
NoSql:
name: "MONGO"
MongoDb:
- connection: "mongodb://${env:DB_USER}:${env:DB_PASS}@localhost:27017/logging.logs"
+ connection: "mongodb://${env:DB_USER}:${env:DB_PASS}@localhost:27017/"
+ databaseName: "logging"
+ collectionName: "logs"
KeyValuePair:
- key: "startTime"
value: "${date:yyyy-MM-dd hh:mm:ss.SSS}" # <1>
diff --git a/src/site/antora/modules/ROOT/examples/manual/appenders/database/nosql-mongo.json b/src/site/antora/modules/ROOT/examples/manual/appenders/database/nosql-mongo.json
index d06b3a190f6..b8bd82389e6 100644
--- a/src/site/antora/modules/ROOT/examples/manual/appenders/database/nosql-mongo.json
+++ b/src/site/antora/modules/ROOT/examples/manual/appenders/database/nosql-mongo.json
@@ -10,7 +10,9 @@
"NoSql": {
"name": "MONGO",
"MongoDb": {
- "connection": "mongodb://${env:DB_USER}:${env:DB_PASS}@localhost:27017/logging.logs"
+ "connection": "mongodb://${env:DB_USER}:${env:DB_PASS}@localhost:27017/",
+ "databaseName": "logging",
+ "collectionName": "logs"
}
}
// end::appender[]
diff --git a/src/site/antora/modules/ROOT/examples/manual/appenders/database/nosql-mongo.properties b/src/site/antora/modules/ROOT/examples/manual/appenders/database/nosql-mongo.properties
index 6a1e188310c..ab06837d8b9 100644
--- a/src/site/antora/modules/ROOT/examples/manual/appenders/database/nosql-mongo.properties
+++ b/src/site/antora/modules/ROOT/examples/manual/appenders/database/nosql-mongo.properties
@@ -22,7 +22,9 @@ appender.0.layout.type = JsonTemplateLayout
appender.1.type = NoSql
appender.1.name = MONGO
appender.1.provider.type = MongoDB
-appender.1.provider.connection = mongodb://${env:DB_USER}:${env:DB_PASS}@localhost:27017/logging.logs
+appender.1.provider.connection = mongodb://${env:DB_USER}:${env:DB_PASS}@localhost:27017/
+appender.1.provider.databaseName = logging
+appender.1.provider.collectionName = logs
# end::appender[]
# tag::loggers[]
rootLogger.level = INFO
diff --git a/src/site/antora/modules/ROOT/examples/manual/appenders/database/nosql-mongo.xml b/src/site/antora/modules/ROOT/examples/manual/appenders/database/nosql-mongo.xml
index 31fe89f2f19..d5a22a1134b 100644
--- a/src/site/antora/modules/ROOT/examples/manual/appenders/database/nosql-mongo.xml
+++ b/src/site/antora/modules/ROOT/examples/manual/appenders/database/nosql-mongo.xml
@@ -27,7 +27,9 @@
-
+
diff --git a/src/site/antora/modules/ROOT/examples/manual/appenders/database/nosql-mongo.yaml b/src/site/antora/modules/ROOT/examples/manual/appenders/database/nosql-mongo.yaml
index f5bd4b66121..86874f8d1a6 100644
--- a/src/site/antora/modules/ROOT/examples/manual/appenders/database/nosql-mongo.yaml
+++ b/src/site/antora/modules/ROOT/examples/manual/appenders/database/nosql-mongo.yaml
@@ -24,7 +24,9 @@ Configuration:
NoSql:
name: "MONGO"
MongoDb:
- connection: "mongodb://${env:DB_USER}:${env:DB_PASS}@localhost:27017/logging.logs"
+ connection: "mongodb://${env:DB_USER}:${env:DB_PASS}@localhost:27017/"
+ databaseName: "logging"
+ collectionName: "logs"
# end::appender[]
Loggers:
# tag::loggers[]
diff --git a/src/site/antora/modules/ROOT/pages/manual/appenders/database.adoc b/src/site/antora/modules/ROOT/pages/manual/appenders/database.adoc
index cf42f454d9a..901c37ba25a 100644
--- a/src/site/antora/modules/ROOT/pages/manual/appenders/database.adoc
+++ b/src/site/antora/modules/ROOT/pages/manual/appenders/database.adoc
@@ -1164,14 +1164,32 @@ It supports the following configuration options:
| Attribute | Type | Default value | Description
| [[MongoDbProvider-attr-connection]]connection
-| https://mongodb.github.io/mongo-java-driver/5.1/apidocs/mongodb-driver-core/com/mongodb/ConnectionString.html[`ConnectionString`]
+| https://www.mongodb.com/docs/manual/reference/connection-string/#standard-connection-string-format[Connection String]
|
|
It specifies the connection URI used to reach the server.
-See
-https://www.mongodb.com/docs/drivers/java/sync/current/fundamentals/connection/connect/#connection-uri[Connection URI documentation]
-for its format.
+**Required**
+
+| [[MongoDbProvider-attr-databaseName]]databaseName
+| `string`
+|
+|
+It specifies the name of the database for the appender to use.
+
+The database name can also be specified in <>.
+If both are provided, this `databaseName` attribute will be used.
+
+**Required**
+
+| [[MongoDbProvider-attr-collectionName]]collectionName
+| `string`
+|
+|
+It specifies the name of the collection for the appender to use.
+
+For backward compatibility, the collection name can also be specified in <> per https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/ConnectionString.html[`ConnectionString` of the MongoDB Java Driver].
+If both are provided, this `collectionName` attribute will be used.
**Required**
@@ -1210,15 +1228,11 @@ It supports the following configuration attributes:
| Attribute | Type | Default value | Description
| [[MongoDb4Provider-attr-connection]]connection
-| https://mongodb.github.io/mongo-java-driver/5.1/apidocs/mongodb-driver-core/com/mongodb/ConnectionString.html[`ConnectionString`]
+| https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/ConnectionString.html[`ConnectionString`]
|
|
It specifies the connection URI used to reach the server.
-See
-https://www.mongodb.com/docs/drivers/java/sync/current/fundamentals/connection/connect/#connection-uri[Connection URI documentation]
-for its format.
-
**Required**
| [[MongoDb4Provider-attr-capped]]capped
From 14adc25a49825b49995d4070210e87a3660f0f38 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?=
Date: Fri, 21 Feb 2025 14:29:53 +0100
Subject: [PATCH 23/42] Add changelog entry (#3066)
---
src/changelog/.2.x.x/3066_fix_bom.xml | 9 +++++++++
1 file changed, 9 insertions(+)
create mode 100644 src/changelog/.2.x.x/3066_fix_bom.xml
diff --git a/src/changelog/.2.x.x/3066_fix_bom.xml b/src/changelog/.2.x.x/3066_fix_bom.xml
new file mode 100644
index 00000000000..56c14c2d16b
--- /dev/null
+++ b/src/changelog/.2.x.x/3066_fix_bom.xml
@@ -0,0 +1,9 @@
+
+
+
+
+ Fix the leak of non-Log4j dependencies in `log4j-bom`
+
From 9bc402e442120dcfbb375919aaabab930bf6876d Mon Sep 17 00:00:00 2001
From: ASF Logging Services RM
Date: Mon, 24 Feb 2025 12:08:02 +0000
Subject: [PATCH 24/42] Update `fast-xml-parser` to version `5.0.6` (#3487)
---
package.json | 2 +-
src/changelog/.2.x.x/update_fast_xml_parser.xml | 8 ++++++++
2 files changed, 9 insertions(+), 1 deletion(-)
create mode 100644 src/changelog/.2.x.x/update_fast_xml_parser.xml
diff --git a/package.json b/package.json
index df223e24ef1..c21bb9ed1b5 100644
--- a/package.json
+++ b/package.json
@@ -4,7 +4,7 @@
"@antora/site-generator-default": "^3.2.0-alpha.4",
"@asciidoctor/tabs": "^1.0.0-beta.6",
"asciidoctor-kroki": "^0.18.1",
- "fast-xml-parser": "^4.3.6",
+ "fast-xml-parser": "^5.0.6",
"handlebars": "^4.7.8"
}
}
diff --git a/src/changelog/.2.x.x/update_fast_xml_parser.xml b/src/changelog/.2.x.x/update_fast_xml_parser.xml
new file mode 100644
index 00000000000..ad03215ba08
--- /dev/null
+++ b/src/changelog/.2.x.x/update_fast_xml_parser.xml
@@ -0,0 +1,8 @@
+
+
+
+ Update `fast-xml-parser` to version `5.0.6`
+
From bf6ef23f0b22220f36720298811c760bc2c3cc8e Mon Sep 17 00:00:00 2001
From: ASF Logging Services RM
Date: Mon, 24 Feb 2025 12:18:26 +0000
Subject: [PATCH 25/42] Update `org.junit:junit-bom` to version `5.12.0`
(#3488)
---
log4j-parent/pom.xml | 2 +-
src/changelog/.2.x.x/update_org_junit_junit_bom.xml | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/log4j-parent/pom.xml b/log4j-parent/pom.xml
index 8a8d9de2397..d7067eabb67 100644
--- a/log4j-parent/pom.xml
+++ b/log4j-parent/pom.xml
@@ -109,7 +109,7 @@
3.6.0
1.37
4.13.2
- 5.11.4
+ 5.12.0
1.9.1
3.9.0
0.2.0
diff --git a/src/changelog/.2.x.x/update_org_junit_junit_bom.xml b/src/changelog/.2.x.x/update_org_junit_junit_bom.xml
index 423686ebd94..cd7a3ddf98b 100644
--- a/src/changelog/.2.x.x/update_org_junit_junit_bom.xml
+++ b/src/changelog/.2.x.x/update_org_junit_junit_bom.xml
@@ -3,6 +3,6 @@
xmlns="https://logging.apache.org/xml/ns"
xsi:schemaLocation="https://logging.apache.org/xml/ns https://logging.apache.org/xml/ns/log4j-changelog-0.xsd"
type="updated">
-
- Update `org.junit:junit-bom` to version `5.11.4`
+
+ Update `org.junit:junit-bom` to version `5.12.0`
From 6f4fab988f0c96dd3f636a570a8c665532a1c7f8 Mon Sep 17 00:00:00 2001
From: ASF Logging Services RM
Date: Mon, 24 Feb 2025 12:21:40 +0000
Subject: [PATCH 26/42] Update `org.awaitility:awaitility` to version `4.3.0`
(#3489)
---
log4j-parent/pom.xml | 2 +-
src/changelog/.2.x.x/update_org_awaitility_awaitility.xml | 8 ++++++++
2 files changed, 9 insertions(+), 1 deletion(-)
create mode 100644 src/changelog/.2.x.x/update_org_awaitility_awaitility.xml
diff --git a/log4j-parent/pom.xml b/log4j-parent/pom.xml
index d7067eabb67..1f2bb262084 100644
--- a/log4j-parent/pom.xml
+++ b/log4j-parent/pom.xml
@@ -64,7 +64,7 @@
2.0.2
2.0.3
3.27.3
- 4.2.2
+ 4.3.0
2.0b6
7.1.0
3.11.19
diff --git a/src/changelog/.2.x.x/update_org_awaitility_awaitility.xml b/src/changelog/.2.x.x/update_org_awaitility_awaitility.xml
new file mode 100644
index 00000000000..00746c8e473
--- /dev/null
+++ b/src/changelog/.2.x.x/update_org_awaitility_awaitility.xml
@@ -0,0 +1,8 @@
+
+
+
+ Update `org.awaitility:awaitility` to version `4.3.0`
+
From dde535f4c405467100508484c5d9ad7cb0564f5e Mon Sep 17 00:00:00 2001
From: ASF Logging Services RM
Date: Wed, 26 Feb 2025 11:21:24 +0000
Subject: [PATCH 27/42] Update `org.slf4j:slf4j-nop` to version `2.0.17`
(#3496)
---
log4j-mongodb/pom.xml | 2 +-
src/changelog/.2.x.x/update_org_slf4j_slf4j_nop.xml | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/log4j-mongodb/pom.xml b/log4j-mongodb/pom.xml
index 822f4d382db..2d45d209fec 100644
--- a/log4j-mongodb/pom.xml
+++ b/log4j-mongodb/pom.xml
@@ -31,7 +31,7 @@
org.apache.logging.log4j.core
5.3.1
- 2.0.16
+ 2.0.17
diff --git a/src/changelog/.2.x.x/update_org_slf4j_slf4j_nop.xml b/src/changelog/.2.x.x/update_org_slf4j_slf4j_nop.xml
index 2083a3efd68..9c4f22681eb 100644
--- a/src/changelog/.2.x.x/update_org_slf4j_slf4j_nop.xml
+++ b/src/changelog/.2.x.x/update_org_slf4j_slf4j_nop.xml
@@ -3,6 +3,6 @@
xmlns="https://logging.apache.org/xml/ns"
xsi:schemaLocation="https://logging.apache.org/xml/ns https://logging.apache.org/xml/ns/log4j-changelog-0.xsd"
type="updated">
-
- Update `org.slf4j:slf4j-nop` to version `2.0.16`
+
+ Update `org.slf4j:slf4j-nop` to version `2.0.17`
From 56d14a4d17ad1a9ce7534d80e54512cf38a4ddb4 Mon Sep 17 00:00:00 2001
From: ASF Logging Services RM
Date: Wed, 26 Feb 2025 11:21:58 +0000
Subject: [PATCH 28/42] Update `org.slf4j:slf4j-nop` to version `2.0.17`
(#3490)
---
log4j-mongodb4/pom.xml | 2 +-
src/changelog/.2.x.x/update_org_slf4j_slf4j_nop.xml | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/log4j-mongodb4/pom.xml b/log4j-mongodb4/pom.xml
index 9115fe4175f..753375bf3c1 100644
--- a/log4j-mongodb4/pom.xml
+++ b/log4j-mongodb4/pom.xml
@@ -36,7 +36,7 @@
org.apache.logging.log4j.core
4.11.5
- 2.0.16
+ 2.0.17
diff --git a/src/changelog/.2.x.x/update_org_slf4j_slf4j_nop.xml b/src/changelog/.2.x.x/update_org_slf4j_slf4j_nop.xml
index 9c4f22681eb..534c7b98844 100644
--- a/src/changelog/.2.x.x/update_org_slf4j_slf4j_nop.xml
+++ b/src/changelog/.2.x.x/update_org_slf4j_slf4j_nop.xml
@@ -3,6 +3,6 @@
xmlns="https://logging.apache.org/xml/ns"
xsi:schemaLocation="https://logging.apache.org/xml/ns https://logging.apache.org/xml/ns/log4j-changelog-0.xsd"
type="updated">
-
+
Update `org.slf4j:slf4j-nop` to version `2.0.17`
From ca14c951c99e7c1e2129134a5f1fa664270f225b Mon Sep 17 00:00:00 2001
From: ASF Logging Services RM
Date: Wed, 26 Feb 2025 11:24:45 +0000
Subject: [PATCH 29/42] Update `org.slf4j:slf4j-api` to version `2.0.17`
(#3492)
---
log4j-core-its/pom.xml | 2 +-
src/changelog/.2.x.x/update_org_slf4j_slf4j_api.xml | 8 ++++++++
2 files changed, 9 insertions(+), 1 deletion(-)
create mode 100644 src/changelog/.2.x.x/update_org_slf4j_slf4j_api.xml
diff --git a/log4j-core-its/pom.xml b/log4j-core-its/pom.xml
index 49f770b883c..e657b86c210 100644
--- a/log4j-core-its/pom.xml
+++ b/log4j-core-its/pom.xml
@@ -40,7 +40,7 @@
true
- 2.0.16
+ 2.0.17
diff --git a/src/changelog/.2.x.x/update_org_slf4j_slf4j_api.xml b/src/changelog/.2.x.x/update_org_slf4j_slf4j_api.xml
new file mode 100644
index 00000000000..a8f0ac58632
--- /dev/null
+++ b/src/changelog/.2.x.x/update_org_slf4j_slf4j_api.xml
@@ -0,0 +1,8 @@
+
+
+
+ Update `org.slf4j:slf4j-api` to version `2.0.17`
+
From 2d08264a247148a4292ae9d9bb5a4938459f0756 Mon Sep 17 00:00:00 2001
From: ASF Logging Services RM
Date: Wed, 26 Feb 2025 11:26:07 +0000
Subject: [PATCH 30/42] Update `org.slf4j:slf4j-api` to version `2.0.17`
(#3497)
---
pom.xml | 2 +-
src/changelog/.2.x.x/update_org_slf4j_slf4j_api.xml | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 1fb7338f7ca..941e75b523c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -352,7 +352,7 @@
0.6.0
3.9.0
1.3.15
- 2.0.16
+ 2.0.17
- 2.0.16
+ 2.0.17
diff --git a/src/changelog/.2.x.x/update_org_slf4j_slf4j_api.xml b/src/changelog/.2.x.x/update_org_slf4j_slf4j_api.xml
index 267d2aeb760..1a464ff212a 100644
--- a/src/changelog/.2.x.x/update_org_slf4j_slf4j_api.xml
+++ b/src/changelog/.2.x.x/update_org_slf4j_slf4j_api.xml
@@ -3,6 +3,6 @@
xmlns="https://logging.apache.org/xml/ns"
xsi:schemaLocation="https://logging.apache.org/xml/ns https://logging.apache.org/xml/ns/log4j-changelog-0.xsd"
type="updated">
-
+
Update `org.slf4j:slf4j-api` to version `2.0.17`
From 727c9926285d6ac2fefbe5725ecb6a3b896920a8 Mon Sep 17 00:00:00 2001
From: ASF Logging Services RM
Date: Wed, 26 Feb 2025 11:28:23 +0000
Subject: [PATCH 32/42] Update `org.slf4j:slf4j-api` to version `2.0.17`
(#3499)
---
log4j-slf4j2-impl/pom.xml | 2 +-
src/changelog/.2.x.x/update_org_slf4j_slf4j_api.xml | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/log4j-slf4j2-impl/pom.xml b/log4j-slf4j2-impl/pom.xml
index 453a8f7ae35..f6adab82826 100644
--- a/log4j-slf4j2-impl/pom.xml
+++ b/log4j-slf4j2-impl/pom.xml
@@ -36,7 +36,7 @@
(Refer to the `log4j-to-slf4j` artifact for forwarding the Log4j API to SLF4J.)
- 2.0.16
+ 2.0.17
diff --git a/src/changelog/.2.x.x/update_org_slf4j_slf4j_api.xml b/src/changelog/.2.x.x/update_org_slf4j_slf4j_api.xml
index 1a464ff212a..856135854c9 100644
--- a/src/changelog/.2.x.x/update_org_slf4j_slf4j_api.xml
+++ b/src/changelog/.2.x.x/update_org_slf4j_slf4j_api.xml
@@ -3,6 +3,6 @@
xmlns="https://logging.apache.org/xml/ns"
xsi:schemaLocation="https://logging.apache.org/xml/ns https://logging.apache.org/xml/ns/log4j-changelog-0.xsd"
type="updated">
-
+
Update `org.slf4j:slf4j-api` to version `2.0.17`
From d8cbe77a0a1f60e6708a2bd8f84ea2fb8666c95a Mon Sep 17 00:00:00 2001
From: JWT
Date: Thu, 27 Feb 2025 13:25:15 +0100
Subject: [PATCH 33/42] Bugfix/log4 j 3359 2 (#3502)
* Fixed TypeConverters#LevelConverter javadoc (#3359)
* Moved changelog to .2.x.x per PR Code Review (#3359)
---------
Co-authored-by: Jeff Thomas
---
.../core/config/plugins/convert/TypeConverters.java | 9 ++++++++-
src/changelog/.2.x.x/3359_fix-javadoc.xml | 10 ++++++++++
2 files changed, 18 insertions(+), 1 deletion(-)
create mode 100644 src/changelog/.2.x.x/3359_fix-javadoc.xml
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/convert/TypeConverters.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/convert/TypeConverters.java
index 3e005f07c5d..6c81224c2d6 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/convert/TypeConverters.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/convert/TypeConverters.java
@@ -285,10 +285,17 @@ public Integer convert(final String s) {
}
/**
- * Converts a {@link String} into a Log4j {@link Level}. Returns {@code null} for invalid level names.
+ * Converts a {@link String} into a Log4j {@link Level}.
*/
@Plugin(name = "Level", category = CATEGORY)
public static class LevelConverter implements TypeConverter {
+ /**
+ * {@inheritDoc}
+ * @param s the string to convert
+ * @return the resolved level
+ * @throws NullPointerException if the given value is {@code null}.
+ * @throws IllegalArgumentException if the given argument is not resolvable to a level
+ */
@Override
public Level convert(final String s) {
return Level.valueOf(s);
diff --git a/src/changelog/.2.x.x/3359_fix-javadoc.xml b/src/changelog/.2.x.x/3359_fix-javadoc.xml
new file mode 100644
index 00000000000..4e0a3dffb7e
--- /dev/null
+++ b/src/changelog/.2.x.x/3359_fix-javadoc.xml
@@ -0,0 +1,10 @@
+
+
+
+
+ TypeConverters convert for "Level" incorrectly documented behaviour for invalid value - updated javadoc.
+
+
From 1da1d7e1023e8d47e89f065ef79cc937294aafe3 Mon Sep 17 00:00:00 2001
From: ASF Logging Services RM
Date: Fri, 28 Feb 2025 11:07:25 +0000
Subject: [PATCH 34/42] Update `org.apache.groovy:groovy-bom` to version
`4.0.26` (#3506)
---
log4j-parent/pom.xml | 2 +-
src/changelog/.2.x.x/update_org_apache_groovy_groovy_bom.xml | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/log4j-parent/pom.xml b/log4j-parent/pom.xml
index 1f2bb262084..ca4a63b3680 100644
--- a/log4j-parent/pom.xml
+++ b/log4j-parent/pom.xml
@@ -81,7 +81,7 @@
3.4.4
0.9.0
7.0.5
- 4.0.25
+ 4.0.26
33.4.0-jre
2.2.224
3.0
diff --git a/src/changelog/.2.x.x/update_org_apache_groovy_groovy_bom.xml b/src/changelog/.2.x.x/update_org_apache_groovy_groovy_bom.xml
index ff3bc0aa164..a1dffb7aba0 100644
--- a/src/changelog/.2.x.x/update_org_apache_groovy_groovy_bom.xml
+++ b/src/changelog/.2.x.x/update_org_apache_groovy_groovy_bom.xml
@@ -3,6 +3,6 @@
xmlns="https://logging.apache.org/xml/ns"
xsi:schemaLocation="https://logging.apache.org/xml/ns https://logging.apache.org/xml/ns/log4j-changelog-0.xsd"
type="updated">
-
- Update `org.apache.groovy:groovy-bom` to version `4.0.25`
+
+ Update `org.apache.groovy:groovy-bom` to version `4.0.26`
From 8d05a73372a8f5fcc9df5f3c12413179174e7fb5 Mon Sep 17 00:00:00 2001
From: JWT
Date: Sat, 1 Mar 2025 20:49:17 +0100
Subject: [PATCH 35/42] Fixed AbstractFilterable#isFiltered javadoc (#3300)
(#3456)
* Fixed AbstractFilterable#isFiltered javadoc (#3300)
* Moved changelog to .2.x.x per PR Code Review (#3300)
* Fixed AbstractFilterable#isFiltered javadoc (#3300)
* Moved changelog to .2.x.x per PR Code Review (#3300)
---------
Co-authored-by: Jeff Thomas
---
.../logging/log4j/core/filter/AbstractFilterable.java | 3 ++-
src/changelog/.2.x.x/3300_fix-javadoc.xml | 10 ++++++++++
2 files changed, 12 insertions(+), 1 deletion(-)
create mode 100644 src/changelog/.2.x.x/3300_fix-javadoc.xml
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/AbstractFilterable.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/AbstractFilterable.java
index 138ec820cae..74f0790ad3b 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/AbstractFilterable.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/AbstractFilterable.java
@@ -145,7 +145,8 @@ public boolean hasFilter() {
/**
* Determine if the LogEvent should be processed or ignored.
* @param event The LogEvent.
- * @return true if the LogEvent should be processed.
+ * @return {@code true} if the event is filtered and should be ignored; otherwise, {@code false} if
+ * it should be processed
*/
@Override
public boolean isFiltered(final LogEvent event) {
diff --git a/src/changelog/.2.x.x/3300_fix-javadoc.xml b/src/changelog/.2.x.x/3300_fix-javadoc.xml
new file mode 100644
index 00000000000..5d84b4325cb
--- /dev/null
+++ b/src/changelog/.2.x.x/3300_fix-javadoc.xml
@@ -0,0 +1,10 @@
+
+
+
+
+ Corrected @return javadoc for AbstractFilterable#isFiltered().
+
+
From ffbdda56a517b6ada137a41464d07b8a10b5f2f6 Mon Sep 17 00:00:00 2001
From: Jeff Thomas
Date: Wed, 12 Feb 2025 14:07:22 +0100
Subject: [PATCH 36/42] Removed 'patternFlags' @PluginAttribute from
RegexFilter @PluginFactory createFilter. (#3086)
---
.../log4j/core/filter/RegexFilter.java | 77 +++++++++++--------
...remove_patternflags_from_PluginFactory.xml | 10 +++
2 files changed, 53 insertions(+), 34 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 62d41b31f59..84cdbfad47b 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;
@@ -29,7 +26,6 @@
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.message.Message;
import org.apache.logging.log4j.message.MessageFormatMessage;
@@ -43,7 +39,6 @@
@Plugin(name = "RegexFilter", category = Node.CATEGORY, elementType = Filter.ELEMENT_TYPE, printObject = true)
public final class RegexFilter extends AbstractFilter {
- private static final int DEFAULT_PATTERN_FLAGS = 0;
private final Pattern pattern;
private final boolean useRawMessage;
@@ -110,10 +105,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();
}
/**
@@ -123,6 +115,40 @@ 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 {@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.
+ * @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
@@ -138,40 +164,23 @@ public String toString() {
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 {
+ boolean raw = Boolean.TRUE.equals(useRawMsg);
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)
- 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 d5d7aed754861bfca05d0487aa9981a9237f1cef Mon Sep 17 00:00:00 2001
From: Jeff Thomas
Date: Mon, 17 Feb 2025 17:05:26 +0100
Subject: [PATCH 37/42] 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 | 11 +-
.../log4j/core/filter/AbstractFilter.java | 33 +-
.../log4j/core/filter/RegexFilter.java | 358 ++++++++++++++----
.../logging/log4j/core/util/Builder.java | 4 +
...remove_patternflags_from_PluginFactory.xml | 0
5 files changed, 328 insertions(+), 78 deletions(-)
rename src/changelog/{2.25.0 => .2.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 671d998258b..e5801342dd6 100644
--- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/RegexFilterTest.java
+++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/RegexFilterTest.java
@@ -19,6 +19,7 @@
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;
@@ -43,13 +44,17 @@ static void before() {
@Test
void testRegexFilterDoesNotThrowWithAllTheParametersExceptRegexEqualNull() {
assertDoesNotThrow(() -> {
- RegexFilter.createFilter(".* test .*", null, null, null, null);
+ RegexFilter.newBuilder().setRegex(".* test .*").build();
});
}
@Test
void testThresholds() throws Exception {
- RegexFilter filter = RegexFilter.createFilter(".* test .*", null, false, null, null);
+ RegexFilter filter = RegexFilter.newBuilder()
+ .setRegex(".* test .*")
+ .setUseRawMsg(false)
+ .build();
+ assertNotNull(filter);
filter.start();
assertTrue(filter.isStarted());
assertSame(
@@ -65,7 +70,7 @@ void testThresholds() throws Exception {
.setMessage(new SimpleMessage("test")) //
.build();
assertSame(Filter.Result.DENY, filter.filter(event));
- filter = RegexFilter.createFilter(null, null, false, null, null);
+ filter = RegexFilter.newBuilder().build();
assertNull(filter);
}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/AbstractFilter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/AbstractFilter.java
index 397390bcbc3..05b1e6b275b 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/AbstractFilter.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/AbstractFilter.java
@@ -16,6 +16,8 @@
*/
package org.apache.logging.log4j.core.filter;
+import java.util.Objects;
+import java.util.Optional;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.core.AbstractLifeCycle;
@@ -43,16 +45,30 @@ public abstract static class AbstractFilterBuilder builder) {
+
+ Objects.requireNonNull(builder, "The 'builder' argument cannot be null.");
+
+ this.onMatch = Optional.ofNullable(builder.onMatch).orElse(Result.NEUTRAL);
+ this.onMismatch = Optional.ofNullable(builder.onMismatch).orElse(Result.DENY);
+ }
+
@Override
protected boolean equalsImpl(final Object obj) {
if (this == obj) {
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/RegexFilter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/RegexFilter.java
index 84cdbfad47b..d26d157bcc7 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.MessageFormatMessage;
import org.apache.logging.log4j.message.ParameterizedMessage;
@@ -39,53 +44,132 @@
@Plugin(name = "RegexFilter", category = Node.CATEGORY, elementType = Filter.ELEMENT_TYPE, printObject = true)
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 = targetMessageTest(msg);
- return filter(text);
+ return filter(getMessageTextByType(msg));
}
+ /** {@inheritDoc} */
@Override
public Result filter(final LogEvent event) {
- final String text = targetMessageTest(event.getMessage());
- 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;
+ }
+ return pattern.matcher(msg).matches() ? onMatch : onMismatch;
}
- // While `Message#getFormat()` is broken in general, it still makes sense for certain types.
- // Hence, suppress the deprecation warning.
+ /**
+ * Tests the filter pattern against the given Log4j {@code Message}.
+ *
+ * If the raw-message flag is enabled and message is an instance of the following, the raw message format
+ * will be returned.
+ *
+ *
+ * - {@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 targetMessageTest(final Message message) {
+ private String getMessageTextByType(final Message message) {
return useRawMessage
&& (message instanceof ParameterizedMessage
|| message instanceof StringFormattedMessage
@@ -95,92 +179,220 @@ private String targetMessageTest(final Message message) {
: message.getFormattedMessage();
}
- private Result filter(final String msg) {
- if (msg == null) {
- return onMismatch;
- }
- final Matcher m = pattern.matcher(msg);
- return m.matches() ? onMatch : onMismatch;
- }
-
@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 {@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.
- * @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];
- return createFilter(regex, useRawMsg, match, mismatch);
+ /**
+ * @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;
+ }
+
+ /**
+ * 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 10bc1a9f52e..8a772dc7330 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Builder.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Builder.java
@@ -44,6 +44,10 @@ public interface Builder {
*/
T build();
+ /**
+ * Validates that the builder is properly configured to build.
+ * @return {@code true} if the builder configuration is valid; otherwise, {@code false}
+ */
default boolean isValid() {
return PluginBuilder.validateFields(this, getErrorPrefix());
}
diff --git a/src/changelog/2.25.0/3086_change_RegexFilter_remove_patternflags_from_PluginFactory.xml b/src/changelog/.2.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/.2.x.x/3086_change_RegexFilter_remove_patternflags_from_PluginFactory.xml
From 6e7e82dbd7d810ae98c68a0880b30504ebad3887 Mon Sep 17 00:00:00 2001
From: Jeff Thomas
Date: Mon, 17 Feb 2025 17:47:17 +0100
Subject: [PATCH 38/42] 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 e5801342dd6..6cb07a5f7df 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;
@@ -87,9 +90,18 @@ void testDotAllPattern() throws Exception {
}
@Test
- void testNoMsg() throws Exception {
- final RegexFilter filter = RegexFilter.createFilter(".* test .*", null, false, null, null);
+ void testNoMsg() {
+
+ final RegexFilter filter =
+ RegexFilter.newBuilder()
+ .setRegex(".* test .*")
+ .setUseRawMsg(false)
+ .build();
+
+ assertNotNull(filter);
+
filter.start();
+
assertTrue(filter.isStarted());
assertSame(Filter.Result.DENY, filter.filter(null, Level.DEBUG, null, (Object) null, null));
assertSame(Filter.Result.DENY, filter.filter(null, Level.DEBUG, null, (Message) null, null));
@@ -97,28 +109,112 @@ void testNoMsg() throws Exception {
}
@Test
- void testParameterizedMsg() throws Exception {
+ void testParameterizedMsg() {
final String msg = "params {} {}";
final Object[] params = {"foo", "bar"};
// match against raw message
- final RegexFilter rawFilter = RegexFilter.createFilter(
- "params \\{\\} \\{\\}",
- null,
- true, // useRawMsg
- Result.ACCEPT,
- Result.DENY);
+ final RegexFilter rawFilter =
+ RegexFilter.newBuilder()
+ .setRegex("params \\{\\} \\{\\}")
+ .setUseRawMsg(true)
+ .setOnMatch(Result.ACCEPT)
+ .setOnMismatch(Result.DENY)
+ .build();
+
+ assertNotNull(rawFilter);
+
final Result rawResult = rawFilter.filter(null, null, null, msg, params);
assertThat(rawResult, equalTo(Result.ACCEPT));
// match against formatted message
- final RegexFilter fmtFilter = RegexFilter.createFilter(
- "params foo bar",
- null,
- false, // useRawMsg
- Result.ACCEPT,
- Result.DENY);
+ final RegexFilter fmtFilter =
+ RegexFilter.newBuilder()
+ .setRegex("params foo bar")
+ .setUseRawMsg(false)
+ .setOnMatch(Result.ACCEPT)
+ .setOnMismatch(Result.DENY).build();
+
+ assertNotNull(fmtFilter);
+
final Result fmtResult = fmtFilter.filter(null, null, null, msg, params);
assertThat(fmtResult, equalTo(Result.ACCEPT));
}
+
+ /**
+ * A builder with no 'regex' expression should both be invalid and return null on 'build()'.
+ */
+ @Test
+ void testWithValidRegex() {
+
+ final String regex = "^[a-zA-Z0-9_]+$"; // matches alphanumeric with underscores
+
+ final RegexFilter.Builder builder =
+ RegexFilter.newBuilder().setRegex(regex).setUseRawMsg(false).setOnMatch(Result.ACCEPT).setOnMismatch(Result.DENY);
+
+ 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 d26d157bcc7..6c842e66593 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;
@@ -44,9 +45,6 @@
@Plugin(name = "RegexFilter", category = Node.CATEGORY, elementType = Filter.ELEMENT_TYPE, printObject = true)
public final class RegexFilter extends AbstractFilter {
- /** The regular-expression. */
- private final String regex;
-
/** The pattern compiled from the regular-expression. */
private final Pattern pattern;
@@ -62,13 +60,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);
}
}
@@ -77,7 +74,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();
}
/**
@@ -136,7 +133,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;
}
@@ -181,7 +178,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();
}
/**
@@ -251,7 +248,11 @@ public Builder setUseRawMsg(final boolean useRawMsg) {
*/
@Override
public boolean isValid() {
- return (regex != null);
+ boolean valid = true;
+ if (!isRegexValid()) {
+ valid = false;
+ }
+ return valid;
}
/**
@@ -273,6 +274,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;
+ }
}
/*
@@ -306,7 +326,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 283dcfffc09534d5bd3fd950a688929193114947 Mon Sep 17 00:00:00 2001
From: Jeff Thomas
Date: Sun, 2 Mar 2025 16:11:09 +0100
Subject: [PATCH 39/42] 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 | 24 +-
.../log4j/core/filter/RegexFilterTest.java | 57 +++--
.../log4j/core/filter/RegexFilter.java | 217 ++++++++++--------
4 files changed, 159 insertions(+), 140 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 8996b9f2f16..fc42abe2937 100644
--- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/AbstractFilterTest.java
+++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/AbstractFilterTest.java
@@ -34,7 +34,6 @@ class AbstractFilterTest {
@Test
void testUnrolledBackwardsCompatible() {
final ConcreteFilter filter = new ConcreteFilter();
- final Filter.Result expected = Filter.Result.DENY;
verifyMethodsWithUnrolledVarargs(filter, Filter.Result.DENY);
filter.testResult = Filter.Result.ACCEPT;
diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/AbstractFilterableTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/AbstractFilterableTest.java
index 4f117a1ccf5..bd3d3f51b20 100644
--- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/AbstractFilterableTest.java
+++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/AbstractFilterableTest.java
@@ -54,7 +54,7 @@ void testAddMultipleSimpleFilters() {
// into a CompositeFilter.class
filterable.addFilter(filter);
assertInstanceOf(CompositeFilter.class, filterable.getFilter());
- assertEquals(2, ((CompositeFilter) filterable.getFilter()).getFilters().size());
+ assertEquals(2, ((CompositeFilter) filterable.getFilter()).getFiltersArray().length);
}
@Test
@@ -67,7 +67,7 @@ void testAddMultipleEqualSimpleFilter() {
// into a CompositeFilter.class
filterable.addFilter(filter);
assertInstanceOf(CompositeFilter.class, filterable.getFilter());
- assertEquals(2, ((CompositeFilter) filterable.getFilter()).getFilters().size());
+ assertEquals(2, ((CompositeFilter) filterable.getFilter()).getFiltersArray().length);
}
@Test
@@ -93,7 +93,7 @@ void testAddMultipleCompositeFilters() {
// into a CompositeFilter.class
filterable.addFilter(compositeFilter);
assertInstanceOf(CompositeFilter.class, filterable.getFilter());
- assertEquals(6, ((CompositeFilter) filterable.getFilter()).getFilters().size());
+ assertEquals(6, ((CompositeFilter) filterable.getFilter()).getFiltersArray().length);
}
@Test
@@ -109,7 +109,7 @@ void testAddSimpleFilterAndCompositeFilter() {
// into a CompositeFilter.class
filterable.addFilter(compositeFilter);
assertInstanceOf(CompositeFilter.class, filterable.getFilter());
- assertEquals(2, ((CompositeFilter) filterable.getFilter()).getFilters().size());
+ assertEquals(2, ((CompositeFilter) filterable.getFilter()).getFiltersArray().length);
}
@Test
@@ -125,7 +125,7 @@ void testAddCompositeFilterAndSimpleFilter() {
// into a CompositeFilter.class
filterable.addFilter(notInCompositeFilterFilter);
assertInstanceOf(CompositeFilter.class, filterable.getFilter());
- assertEquals(3, ((CompositeFilter) filterable.getFilter()).getFilters().size());
+ assertEquals(3, ((CompositeFilter) filterable.getFilter()).getFiltersArray().length);
}
@Test
@@ -170,7 +170,7 @@ void testRemoveSimpleEqualFilterFromMultipleSimpleFilters() {
filterable.addFilter(filterCopy);
filterable.removeFilter(filterCopy);
assertInstanceOf(CompositeFilter.class, filterable.getFilter());
- assertEquals(2, ((CompositeFilter) filterable.getFilter()).getFilters().size());
+ assertEquals(2, ((CompositeFilter) filterable.getFilter()).getFiltersArray().length);
filterable.removeFilter(filterCopy);
assertEquals(filterOriginal, filterable.getFilter());
filterable.removeFilter(filterOriginal);
@@ -224,7 +224,7 @@ void testRemoveSimpleFilterFromCompositeAndSimpleFilter() {
// should not remove internal filter of compositeFilter
filterable.removeFilter(anotherFilter);
assertInstanceOf(CompositeFilter.class, filterable.getFilter());
- assertEquals(2, ((CompositeFilter) filterable.getFilter()).getFilters().size());
+ assertEquals(2, ((CompositeFilter) filterable.getFilter()).getFiltersArray().length);
}
@Test
@@ -247,9 +247,9 @@ void testRemoveFiltersFromComposite() {
filterable.addFilter(compositeFilter);
filterable.addFilter(anotherFilter);
- assertEquals(3, ((CompositeFilter) filterable.getFilter()).getFilters().size());
+ assertEquals(3, ((CompositeFilter) filterable.getFilter()).getFiltersArray().length);
filterable.removeFilter(filter1);
- assertEquals(2, ((CompositeFilter) filterable.getFilter()).getFilters().size());
+ assertEquals(2, ((CompositeFilter) filterable.getFilter()).getFiltersArray().length);
filterable.removeFilter(filter2);
assertSame(anotherFilter, filterable.getFilter());
}
@@ -274,11 +274,7 @@ public boolean equals(final Object o) {
final EqualFilter that = (EqualFilter) o;
- if (key != null ? !key.equals(that.key) : that.key != null) {
- return false;
- }
-
- return true;
+ return key != null ? key.equals(that.key) : that.key == null;
}
@Override
diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/RegexFilterTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/RegexFilterTest.java
index 6cb07a5f7df..b7a128b5bfb 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;
@@ -92,11 +91,10 @@ 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);
@@ -114,13 +112,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);
@@ -128,12 +125,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);
@@ -149,8 +146,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());
@@ -170,13 +170,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);
@@ -199,7 +198,6 @@ void testBuilderWithoutRegexNotValid() {
assertFalse(builder.isValid());
assertNull(builder.build());
-
}
/**
@@ -215,6 +213,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 6c842e66593..ffe470af485 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,16 +32,23 @@
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.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 {
/** The pattern compiled from the regular-expression. */
@@ -54,12 +60,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 {
@@ -69,14 +79,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}
@@ -85,59 +87,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;
}
/**
@@ -176,6 +242,7 @@ private String getMessageTextByType(final Message message) {
: message.getFormattedMessage();
}
+ /** {@inheritDoc} */
@Override
public String toString() {
return "useRawMessage=" + useRawMessage + ", pattern=" + pattern.toString();
@@ -183,12 +250,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();
}
/**
@@ -203,7 +269,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;
- }
}
/*
@@ -320,11 +357,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();
@@ -368,32 +405,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 6f1358c605ac97b4e07aa72f18f48df76c19035c Mon Sep 17 00:00:00 2001
From: Jeff Thomas
Date: Sun, 2 Mar 2025 19:49:09 +0100
Subject: [PATCH 40/42] 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 69956c1c845..fffd7bfda14 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
@@ -293,11 +293,10 @@ public void evaluate() throws Throwable {
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 ffe470af485..d97f70ea979 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
@@ -66,7 +66,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.");
}
@@ -317,8 +317,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 c7f89d8c56c226909a64bbb38b9a4ef0030795be Mon Sep 17 00:00:00 2001
From: Jeff Thomas
Date: Sun, 2 Mar 2025 19:58:38 +0100
Subject: [PATCH 41/42] 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 fffd7bfda14..69956c1c845 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
@@ -293,10 +293,11 @@ public void evaluate() throws Throwable {
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 d6b55746928cfe8c19edb01ea26e726a6ba5a03d Mon Sep 17 00:00:00 2001
From: Jeff Thomas
Date: Sun, 2 Mar 2025 20:21:55 +0100
Subject: [PATCH 42/42] Fix RegexFilterTest (#3086)
---
.../apache/logging/log4j/core/filter/RegexFilterTest.java | 8 +-------
.../org/apache/logging/log4j/core/filter/RegexFilter.java | 6 ++++++
2 files changed, 7 insertions(+), 7 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 b7a128b5bfb..3bf96c607fd 100644
--- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/RegexFilterTest.java
+++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/RegexFilterTest.java
@@ -40,7 +40,7 @@
class RegexFilterTest {
@BeforeAll
static void before() {
- StatusLogger.getLogger().setLevel(Level.OFF);
+ StatusLogger.getLogger().getFallbackListener().setLevel(Level.OFF);
}
@Test
@@ -152,8 +152,6 @@ void testWithValidRegex() {
.setOnMatch(Result.ACCEPT)
.setOnMismatch(Result.DENY);
- assertTrue(builder.isValid());
-
final RegexFilter filter = builder.build();
assertNotNull(filter);
@@ -195,8 +193,6 @@ void testBuilderWithoutRegexNotValid() {
final RegexFilter.Builder builder = RegexFilter.newBuilder();
- assertFalse(builder.isValid());
-
assertNull(builder.build());
}
@@ -210,8 +206,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 d97f70ea979..839089b35bb 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/RegexFilter.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/RegexFilter.java
@@ -308,6 +308,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.
*