You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by pk...@apache.org on 2022/06/13 09:07:05 UTC

[logging-log4j2] branch release-2.x updated: [LOG4J2-3534] Fix LevelRangeFilterBuilder to align with log4j1's behavior (#924)

This is an automated email from the ASF dual-hosted git repository.

pkarwasz pushed a commit to branch release-2.x
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git


The following commit(s) were added to refs/heads/release-2.x by this push:
     new c2d049d947 [LOG4J2-3534] Fix LevelRangeFilterBuilder to align with log4j1's behavior (#924)
c2d049d947 is described below

commit c2d049d947da1f650862437df036fea470ff2869
Author: Yohei Ueki <yu...@users.noreply.github.com>
AuthorDate: Mon Jun 13 18:06:57 2022 +0900

    [LOG4J2-3534] Fix LevelRangeFilterBuilder to align with log4j1's behavior (#924)
    
    The current `LevelRangeFilterBuilder` does not take into account the inversion of the level scale between Log4j 1 and Log4j 2: a minimal Log4j 1 level corresponds to a maximal Log4j 2 level and vice versa.
---
 .../builders/filter/LevelRangeFilterBuilder.java   |  21 +-
 .../filter/LevelRangeFilterBuilderTest.java        | 211 +++++++++++++++++++++
 .../config/AbstractLog4j1ConfigurationTest.java    |  29 +++
 .../log4j/config/PropertiesConfigurationTest.java  |  41 +++-
 .../apache/log4j/config/XmlConfigurationTest.java  |   5 +
 .../src/test/resources/LOG4J2-3326.properties      |  49 ++---
 .../log4j-LevelRangeFilter.properties}             |  46 ++---
 .../config-1.2/log4j-LevelRangeFilter.xml          |  32 ++++
 .../log4j/core/filter/LevelRangeFilter.java        |   4 +
 src/changes/changes.xml                            |   3 +
 10 files changed, 384 insertions(+), 57 deletions(-)

diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/LevelRangeFilterBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/LevelRangeFilterBuilder.java
index ca103fe2cb..615b4a0a73 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/LevelRangeFilterBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/LevelRangeFilterBuilder.java
@@ -35,7 +35,10 @@ import org.apache.logging.log4j.core.filter.LevelRangeFilter;
 import org.w3c.dom.Element;
 
 /**
- * Build a Level match failter.
+ * Build a Level range filter.
+ * In this class, order of {@link Level} is log4j1 way, i.e.,
+ * {@link Level#ALL} and {@link Level#OFF} have minimum and maximum order, respectively.
+ * (see: LOG4J2-2315)
  */
 @Plugin(name = "org.apache.log4j.varia.LevelRangeFilter", category = CATEGORY)
 public class LevelRangeFilterBuilder extends AbstractBuilder<Filter> implements FilterBuilder {
@@ -63,7 +66,7 @@ public class LevelRangeFilterBuilder extends AbstractBuilder<Filter> implements
                         levelMax.set(getValueAttribute(currentElement));
                         break;
                     case LEVEL_MIN:
-                        levelMax.set(getValueAttribute(currentElement));
+                        levelMin.set(getValueAttribute(currentElement));
                         break;
                     case ACCEPT_ON_MATCH:
                         acceptOnMatch.set(getBooleanValueAttribute(currentElement));
@@ -83,19 +86,23 @@ public class LevelRangeFilterBuilder extends AbstractBuilder<Filter> implements
     }
 
     private Filter createFilter(String levelMax, String levelMin, boolean acceptOnMatch) {
-        Level max = Level.FATAL;
-        Level min = Level.TRACE;
+        Level max = Level.OFF;
+        Level min = Level.ALL;
         if (levelMax != null) {
-            max = OptionConverter.toLevel(levelMax, org.apache.log4j.Level.FATAL).getVersion2Level();
+            max = OptionConverter.toLevel(levelMax, org.apache.log4j.Level.OFF).getVersion2Level();
         }
         if (levelMin != null) {
-            min = OptionConverter.toLevel(levelMin, org.apache.log4j.Level.DEBUG).getVersion2Level();
+            min = OptionConverter.toLevel(levelMin, org.apache.log4j.Level.ALL).getVersion2Level();
         }
         org.apache.logging.log4j.core.Filter.Result onMatch = acceptOnMatch
                 ? org.apache.logging.log4j.core.Filter.Result.ACCEPT
                 : org.apache.logging.log4j.core.Filter.Result.NEUTRAL;
 
-        return FilterWrapper.adapt(LevelRangeFilter.createFilter(min, max, onMatch,
+        // XXX: LOG4J2-2315
+        // log4j1 order: ALL < TRACE < DEBUG < ... < FATAL < OFF
+        // log4j2 order: ALL > TRACE > DEBUG > ... > FATAL > OFF
+        // So we create as LevelRangeFilter.createFilter(minLevel=max, maxLevel=min, ...)
+        return FilterWrapper.adapt(LevelRangeFilter.createFilter(max, min, onMatch,
                 org.apache.logging.log4j.core.Filter.Result.DENY));
     }
 }
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/builders/filter/LevelRangeFilterBuilderTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/builders/filter/LevelRangeFilterBuilderTest.java
new file mode 100644
index 0000000000..327ea823c2
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/builders/filter/LevelRangeFilterBuilderTest.java
@@ -0,0 +1,211 @@
+/*
+ * 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.log4j.builders.filter;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.io.StringReader;
+import java.util.Properties;
+import java.util.stream.Stream;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.apache.log4j.bridge.FilterWrapper;
+import org.apache.log4j.spi.Filter;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.Filter.Result;
+import org.apache.logging.log4j.core.filter.LevelRangeFilter;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.ArgumentsProvider;
+import org.junit.jupiter.params.provider.ArgumentsSource;
+import org.w3c.dom.Element;
+import org.xml.sax.InputSource;
+
+public class LevelRangeFilterBuilderTest {
+
+    @ParameterizedTest
+    @ArgumentsSource(TestLevelRangeFilterBuilderProvider.class)
+    public void testAcceptOnMatchTrue(TestLevelRangeFilterBuilder builder) throws Exception {
+        LevelRangeFilter levelRangeFilter = builder.build(Level.INFO, Level.ERROR, true);
+
+        assertResult(Result.DENY, levelRangeFilter, Level.ALL);
+        assertResult(Result.DENY, levelRangeFilter, Level.DEBUG);
+        assertResult(Result.ACCEPT, levelRangeFilter, Level.INFO);
+        assertResult(Result.ACCEPT, levelRangeFilter, Level.WARN);
+        assertResult(Result.ACCEPT, levelRangeFilter, Level.ERROR);
+        assertResult(Result.DENY, levelRangeFilter, Level.FATAL);
+        assertResult(Result.DENY, levelRangeFilter, Level.OFF);
+    }
+
+    @ParameterizedTest
+    @ArgumentsSource(TestLevelRangeFilterBuilderProvider.class)
+    public void testAcceptOnMatchFalse(TestLevelRangeFilterBuilder builder) throws Exception {
+        LevelRangeFilter levelRangeFilter = builder.build(Level.INFO, Level.ERROR, false);
+
+        assertResult(Result.DENY, levelRangeFilter, Level.ALL);
+        assertResult(Result.DENY, levelRangeFilter, Level.DEBUG);
+        assertResult(Result.NEUTRAL, levelRangeFilter, Level.INFO);
+        assertResult(Result.NEUTRAL, levelRangeFilter, Level.WARN);
+        assertResult(Result.NEUTRAL, levelRangeFilter, Level.ERROR);
+        assertResult(Result.DENY, levelRangeFilter, Level.FATAL);
+        assertResult(Result.DENY, levelRangeFilter, Level.OFF);
+    }
+
+    @ParameterizedTest
+    @ArgumentsSource(TestLevelRangeFilterBuilderProvider.class)
+    public void testAcceptOnMatchNull(TestLevelRangeFilterBuilder builder) throws Exception {
+        LevelRangeFilter levelRangeFilter = builder.build(Level.INFO, Level.ERROR, null);
+
+        assertResult(Result.DENY, levelRangeFilter, Level.ALL);
+        assertResult(Result.DENY, levelRangeFilter, Level.DEBUG);
+        assertResult(Result.NEUTRAL, levelRangeFilter, Level.INFO);
+        assertResult(Result.NEUTRAL, levelRangeFilter, Level.WARN);
+        assertResult(Result.NEUTRAL, levelRangeFilter, Level.ERROR);
+        assertResult(Result.DENY, levelRangeFilter, Level.FATAL);
+        assertResult(Result.DENY, levelRangeFilter, Level.OFF);
+    }
+
+    @ParameterizedTest
+    @ArgumentsSource(TestLevelRangeFilterBuilderProvider.class)
+    public void testMinLevelNull(TestLevelRangeFilterBuilder builder) throws Exception {
+        LevelRangeFilter levelRangeFilter = builder.build(null, Level.ERROR, true);
+
+        assertResult(Result.ACCEPT, levelRangeFilter, Level.ALL);
+        assertResult(Result.ACCEPT, levelRangeFilter, Level.DEBUG);
+        assertResult(Result.ACCEPT, levelRangeFilter, Level.INFO);
+        assertResult(Result.ACCEPT, levelRangeFilter, Level.WARN);
+        assertResult(Result.ACCEPT, levelRangeFilter, Level.ERROR);
+        assertResult(Result.DENY, levelRangeFilter, Level.FATAL);
+        assertResult(Result.DENY, levelRangeFilter, Level.OFF);
+    }
+
+    @ParameterizedTest
+    @ArgumentsSource(TestLevelRangeFilterBuilderProvider.class)
+    public void testMaxLevelNull(TestLevelRangeFilterBuilder builder) throws Exception {
+        LevelRangeFilter levelRangeFilter = builder.build(Level.INFO, null, true);
+
+        assertResult(Result.DENY, levelRangeFilter, Level.ALL);
+        assertResult(Result.DENY, levelRangeFilter, Level.DEBUG);
+        assertResult(Result.ACCEPT, levelRangeFilter, Level.INFO);
+        assertResult(Result.ACCEPT, levelRangeFilter, Level.WARN);
+        assertResult(Result.ACCEPT, levelRangeFilter, Level.ERROR);
+        assertResult(Result.ACCEPT, levelRangeFilter, Level.FATAL);
+        assertResult(Result.ACCEPT, levelRangeFilter, Level.OFF);
+    }
+
+    @ParameterizedTest
+    @ArgumentsSource(TestLevelRangeFilterBuilderProvider.class)
+    public void testMinMaxLevelSame(TestLevelRangeFilterBuilder builder) throws Exception {
+        LevelRangeFilter levelRangeFilter = builder.build(Level.INFO, Level.INFO, true);
+
+        assertResult(Result.DENY, levelRangeFilter, Level.ALL);
+        assertResult(Result.DENY, levelRangeFilter, Level.DEBUG);
+        assertResult(Result.ACCEPT, levelRangeFilter, Level.INFO);
+        assertResult(Result.DENY, levelRangeFilter, Level.WARN);
+        assertResult(Result.DENY, levelRangeFilter, Level.ERROR);
+        assertResult(Result.DENY, levelRangeFilter, Level.FATAL);
+        assertResult(Result.DENY, levelRangeFilter, Level.OFF);
+    }
+
+    @ParameterizedTest
+    @ArgumentsSource(TestLevelRangeFilterBuilderProvider.class)
+    public void testMinMaxLevelNull(TestLevelRangeFilterBuilder builder) throws Exception {
+        LevelRangeFilter levelRangeFilter = builder.build(null, null, true);
+
+        assertResult(Result.ACCEPT, levelRangeFilter, Level.ALL);
+        assertResult(Result.ACCEPT, levelRangeFilter, Level.DEBUG);
+        assertResult(Result.ACCEPT, levelRangeFilter, Level.INFO);
+        assertResult(Result.ACCEPT, levelRangeFilter, Level.WARN);
+        assertResult(Result.ACCEPT, levelRangeFilter, Level.ERROR);
+        assertResult(Result.ACCEPT, levelRangeFilter, Level.FATAL);
+        assertResult(Result.ACCEPT, levelRangeFilter, Level.OFF);
+    }
+
+    private static void assertResult(Result expected, LevelRangeFilter filter, Level level) {
+        assertSame(expected, filter.filter(null, level, null, (Object) null, null));
+    }
+
+    private static class TestLevelRangeFilterBuilderProvider implements ArgumentsProvider {
+
+        @Override
+        public Stream<? extends Arguments> provideArguments(ExtensionContext extensionContext) {
+            return Stream.of(
+                    Arguments.of(new TestLevelRangeFilterFromXmlBuilder()),
+                    Arguments.of(new TestLevelRangeFilterFromPropertyBuilder())
+            );
+        }
+    }
+
+    private interface TestLevelRangeFilterBuilder {
+
+        LevelRangeFilter build(Level levelMin, Level levelMax, Boolean acceptOnMatch) throws Exception;
+    }
+
+    private static class TestLevelRangeFilterFromXmlBuilder implements TestLevelRangeFilterBuilder {
+
+        @Override
+        public LevelRangeFilter build(Level levelMin, Level levelMax, Boolean acceptOnMatch) throws Exception {
+            LevelRangeFilterBuilder builder = new LevelRangeFilterBuilder();
+            Filter filter = builder.parse(generateTestXml(levelMin, levelMax, acceptOnMatch), null);
+            org.apache.logging.log4j.core.Filter wrappedFilter = ((FilterWrapper) filter).getFilter();
+            return (LevelRangeFilter) wrappedFilter;
+        }
+
+        private static Element generateTestXml(Level levelMin, Level levelMax, Boolean acceptOnMatch) throws Exception {
+
+            StringBuilder sb = new StringBuilder();
+            sb.append("<filter class=\"org.apache.log4j.varia.LevelRangeFilter\">\n");
+            if (levelMin != null) {
+                sb.append(String.format("<param name=\"LevelMin\" value=\"%s\"/>\n", levelMin));
+            }
+            if (levelMax != null) {
+                sb.append(String.format("<param name=\"LevelMax\" value=\"%s\"/>\n", levelMax));
+            }
+            if (acceptOnMatch != null) {
+                sb.append(String.format("<param name=\"AcceptOnMatch\" value=\"%b\"/>\n", acceptOnMatch));
+            }
+            sb.append("</filter>");
+
+            return DocumentBuilderFactory.newInstance().newDocumentBuilder()
+                    .parse(new InputSource(new StringReader(sb.toString())))
+                    .getDocumentElement();
+        }
+    }
+
+    private static class TestLevelRangeFilterFromPropertyBuilder implements TestLevelRangeFilterBuilder {
+
+        @Override
+        public LevelRangeFilter build(Level levelMin, Level levelMax, Boolean acceptOnMatch) {
+            Properties properties = new Properties();
+            if (levelMin != null) {
+                properties.setProperty("foobar.levelMin", levelMin.name());
+            }
+            if (levelMax != null) {
+                properties.setProperty("foobar.levelMax", levelMax.name());
+            }
+            if (acceptOnMatch != null) {
+                properties.setProperty("foobar.acceptOnMatch", acceptOnMatch.toString());
+            }
+            LevelRangeFilterBuilder builder = new LevelRangeFilterBuilder("foobar", properties);
+            Filter filter = builder.parse(null);
+            org.apache.logging.log4j.core.Filter wrappedFilter = ((FilterWrapper) filter).getFilter();
+            return (LevelRangeFilter) wrappedFilter;
+        }
+    }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/AbstractLog4j1ConfigurationTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/AbstractLog4j1ConfigurationTest.java
index 34fdb03d5b..566d768a61 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/config/AbstractLog4j1ConfigurationTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/AbstractLog4j1ConfigurationTest.java
@@ -611,4 +611,33 @@ public abstract class AbstractLog4j1ConfigurationTest {
         assertEquals(11, defaultRolloverStrategy.getMinIndex());
         assertEquals(20, defaultRolloverStrategy.getMaxIndex());
     }
+
+    protected void testLevelRangeFilter() throws Exception {
+        try (final LoggerContext ctx = configure("config-1.2/log4j-LevelRangeFilter")) {
+            final Configuration config = ctx.getConfiguration();
+            final Logger logger = LogManager.getLogger(PropertiesConfigurationTest.class);
+            // List appender
+            final Appender appender = config.getAppender("LIST");
+            assertNotNull(appender);
+            final ListAppender legacyAppender = (ListAppender) ((Adapter) appender).getAppender();
+            // deny
+            logger.trace("TRACE");
+            assertEquals(0, legacyAppender.getEvents().size());
+            // deny
+            logger.debug("DEBUG");
+            assertEquals(0, legacyAppender.getEvents().size());
+            // accept
+            logger.info("INFO");
+            assertEquals(1, legacyAppender.getEvents().size());
+            // accept
+            logger.warn("WARN");
+            assertEquals(2, legacyAppender.getEvents().size());
+            // accept
+            logger.error("ERROR");
+            assertEquals(3, legacyAppender.getEvents().size());
+            // deny
+            logger.fatal("FATAL");
+            assertEquals(3, legacyAppender.getEvents().size());
+        }
+    }
 }
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesConfigurationTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesConfigurationTest.java
index 219848abba..98ce9dfbc7 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesConfigurationTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesConfigurationTest.java
@@ -125,10 +125,38 @@ public class PropertiesConfigurationTest extends AbstractLog4j1ConfigurationTest
             final Filterable filterable = (Filterable) appender;
             final CompositeFilter filter = (CompositeFilter) filterable.getFilter();
             final org.apache.logging.log4j.core.Filter[] filters = filter.getFiltersArray();
-            final LevelRangeFilter customFilterReal = (LevelRangeFilter) filters[0];
-            assertEquals(Level.ALL, customFilterReal.getMinLevel());
-            final LevelRangeFilter defaultFilter = (LevelRangeFilter) filters[1];
-            assertEquals(Level.TRACE, defaultFilter.getMinLevel());
+            final LevelRangeFilter filter1 = (LevelRangeFilter) filters[0];
+            // XXX: LOG4J2-2315
+            assertEquals(Level.OFF, filter1.getMinLevel());
+            assertEquals(Level.ALL, filter1.getMaxLevel());
+            final LevelRangeFilter filter2 = (LevelRangeFilter) filters[1];
+            assertEquals(Level.ERROR, filter2.getMinLevel());
+            assertEquals(Level.INFO, filter2.getMaxLevel());
+            final LevelRangeFilter filter3 = (LevelRangeFilter) filters[2];
+            assertEquals(Level.OFF, filter3.getMinLevel());
+            assertEquals(Level.ALL, filter3.getMaxLevel());
+
+            final ListAppender legacyAppender = (ListAppender) ((AppenderAdapter.Adapter) appender).getAppender();
+            final Logger logger = LogManager.getLogger(PropertiesConfigurationTest.class);
+
+            // deny
+            logger.trace("TRACE");
+            assertEquals(0, legacyAppender.getEvents().size());
+            // deny
+            logger.debug("DEBUG");
+            assertEquals(0, legacyAppender.getEvents().size());
+            // accept
+            logger.info("INFO");
+            assertEquals(1, legacyAppender.getEvents().size());
+            // accept
+            logger.warn("WARN");
+            assertEquals(2, legacyAppender.getEvents().size());
+            // accept
+            logger.error("ERROR");
+            assertEquals(3, legacyAppender.getEvents().size());
+            // deny
+            logger.fatal("FATAL");
+            assertEquals(3, legacyAppender.getEvents().size());
       }
     }
 
@@ -326,4 +354,9 @@ public class PropertiesConfigurationTest extends AbstractLog4j1ConfigurationTest
         }
     }
 
+    @Override
+    @Test
+    public void testLevelRangeFilter() throws Exception {
+        super.testLevelRangeFilter();
+    }
 }
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlConfigurationTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlConfigurationTest.java
index bec639f202..f314e1bee2 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlConfigurationTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlConfigurationTest.java
@@ -202,4 +202,9 @@ public class XmlConfigurationTest extends AbstractLog4j1ConfigurationTest {
         }
     }
 
+    @Override
+    @Test
+    public void testLevelRangeFilter() throws Exception {
+        super.testLevelRangeFilter();
+    }
 }
diff --git a/log4j-1.2-api/src/test/resources/LOG4J2-3326.properties b/log4j-1.2-api/src/test/resources/LOG4J2-3326.properties
index e21d0d394c..492aba876b 100644
--- a/log4j-1.2-api/src/test/resources/LOG4J2-3326.properties
+++ b/log4j-1.2-api/src/test/resources/LOG4J2-3326.properties
@@ -1,23 +1,26 @@
-#
-# 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.
-#
-
-log4j.appender.CUSTOM=org.apache.log4j.CustomNoopAppender
-log4j.appender.CUSTOM.filter.1=org.apache.log4j.varia.LevelRangeFilter
-log4j.appender.CUSTOM.filter.1.levelMin=ALL
-log4j.appender.CUSTOM.filter.2=org.apache.log4j.varia.LevelRangeFilter
-
-log4j.rootLogger=trace, CUSTOM
+#
+# 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.
+#
+
+log4j.appender.CUSTOM=org.apache.log4j.ListAppender
+log4j.appender.CUSTOM.filter.1=org.apache.log4j.varia.LevelRangeFilter
+log4j.appender.CUSTOM.filter.1.levelMin=ALL
+log4j.appender.CUSTOM.filter.2=org.apache.log4j.varia.LevelRangeFilter
+log4j.appender.CUSTOM.filter.2.levelMin=INFO
+log4j.appender.CUSTOM.filter.2.levelMax=ERROR
+log4j.appender.CUSTOM.filter.3=org.apache.log4j.varia.LevelRangeFilter
+
+log4j.rootLogger=trace, CUSTOM
diff --git a/log4j-1.2-api/src/test/resources/LOG4J2-3326.properties b/log4j-1.2-api/src/test/resources/config-1.2/log4j-LevelRangeFilter.properties
similarity index 73%
copy from log4j-1.2-api/src/test/resources/LOG4J2-3326.properties
copy to log4j-1.2-api/src/test/resources/config-1.2/log4j-LevelRangeFilter.properties
index e21d0d394c..1ba8c5bcb1 100644
--- a/log4j-1.2-api/src/test/resources/LOG4J2-3326.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-LevelRangeFilter.properties
@@ -1,23 +1,23 @@
-#
-# 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.
-#
-
-log4j.appender.CUSTOM=org.apache.log4j.CustomNoopAppender
-log4j.appender.CUSTOM.filter.1=org.apache.log4j.varia.LevelRangeFilter
-log4j.appender.CUSTOM.filter.1.levelMin=ALL
-log4j.appender.CUSTOM.filter.2=org.apache.log4j.varia.LevelRangeFilter
-
-log4j.rootLogger=trace, CUSTOM
+#
+# 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.
+#
+
+log4j.appender.LIST = org.apache.log4j.ListAppender
+log4j.appender.LIST.filter.1 = org.apache.log4j.varia.LevelRangeFilter
+log4j.appender.LIST.filter.1.LevelMin = INFO
+log4j.appender.LIST.filter.1.LevelMax = ERROR
+log4j.appender.LIST.filter.1.AcceptOnMatch = false
+log4j.rootLogger = debug, LIST
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-LevelRangeFilter.xml b/log4j-1.2-api/src/test/resources/config-1.2/log4j-LevelRangeFilter.xml
new file mode 100644
index 0000000000..87db868e32
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-LevelRangeFilter.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+  <appender name="LIST" class="org.apache.log4j.ListAppender">
+    <filter class="org.apache.log4j.varia.LevelRangeFilter">
+      <param name="levelMin" value="INFO"/>
+      <param name="levelMax" value="ERROR"/>
+      <param name="AcceptOnMatch" value="false"/>
+    </filter>
+  </appender>
+
+  <root>
+    <priority value="debug"/>
+    <appender-ref ref="LIST"/>
+  </root>
+</log4j:configuration>
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/LevelRangeFilter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/LevelRangeFilter.java
index 0e79b8508f..18b6d0be5c 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/LevelRangeFilter.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/LevelRangeFilter.java
@@ -180,6 +180,10 @@ public final class LevelRangeFilter extends AbstractFilter {
         return minLevel;
     }
 
+    public Level getMaxLevel() {
+        return maxLevel;
+    }
+
     @Override
     public String toString() {
         return minLevel.toString();
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 943729ec6e..2533905c85 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -30,6 +30,9 @@
          - "remove" - Removed
     -->
     <release version="2.18.0" date="2022-MM-DD" description="GA Release 2.18.0">
+      <action issue="LOG4J2-3534" dev="yueki1993" type="fix">
+        Fix LevelRangeFilterBuilder to align with log4j1's behavior.
+      </action>
       <action issue="LOG4J2-3527" dev="rgoers" type="fix">
         Don't use Paths.get() to avoid circular file systems.
       </action>