Skip to content

Commit 3981d69

Browse files
committed
[LOG4J2-3534] Fix LevelRangeFilterBuilder to align with log4j1's behavior
1 parent a3e8e21 commit 3981d69

File tree

7 files changed

+319
-7
lines changed

7 files changed

+319
-7
lines changed

log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/LevelRangeFilterBuilder.java

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,10 @@
3535
import org.w3c.dom.Element;
3636

3737
/**
38-
* Build a Level match failter.
38+
* Build a Level range filter.
39+
* In this class, order of {@link Level} is log4j1 way, i.e.,
40+
* {@link Level#ALL} and {@link Level#OFF} have minimum and maximum order, respectively.
41+
* (see: LOG4J2-2315)
3942
*/
4043
@Plugin(name = "org.apache.log4j.varia.LevelRangeFilter", category = CATEGORY)
4144
public class LevelRangeFilterBuilder extends AbstractBuilder<Filter> implements FilterBuilder {
@@ -63,7 +66,7 @@ public Filter parse(Element filterElement, XmlConfiguration config) {
6366
levelMax.set(getValueAttribute(currentElement));
6467
break;
6568
case LEVEL_MIN:
66-
levelMax.set(getValueAttribute(currentElement));
69+
levelMin.set(getValueAttribute(currentElement));
6770
break;
6871
case ACCEPT_ON_MATCH:
6972
acceptOnMatch.set(getBooleanValueAttribute(currentElement));
@@ -83,19 +86,23 @@ public Filter parse(PropertiesConfiguration config) {
8386
}
8487

8588
private Filter createFilter(String levelMax, String levelMin, boolean acceptOnMatch) {
86-
Level max = Level.FATAL;
87-
Level min = Level.TRACE;
89+
Level max = Level.OFF;
90+
Level min = Level.ALL;
8891
if (levelMax != null) {
89-
max = OptionConverter.toLevel(levelMax, org.apache.log4j.Level.FATAL).getVersion2Level();
92+
max = OptionConverter.toLevel(levelMax, org.apache.log4j.Level.OFF).getVersion2Level();
9093
}
9194
if (levelMin != null) {
92-
min = OptionConverter.toLevel(levelMin, org.apache.log4j.Level.DEBUG).getVersion2Level();
95+
min = OptionConverter.toLevel(levelMin, org.apache.log4j.Level.ALL).getVersion2Level();
9396
}
9497
org.apache.logging.log4j.core.Filter.Result onMatch = acceptOnMatch
9598
? org.apache.logging.log4j.core.Filter.Result.ACCEPT
9699
: org.apache.logging.log4j.core.Filter.Result.NEUTRAL;
97100

98-
return FilterWrapper.adapt(LevelRangeFilter.createFilter(min, max, onMatch,
101+
// XXX: LOG4J2-2315
102+
// log4j1 order: ALL < TRACE < DEBUG < ... < FATAL < OFF
103+
// log4j2 order: ALL > TRACE > DEBUG > ... > FATAL > OFF
104+
// So we create as LevelRangeFilter.createFilter(minLevel=max, maxLevel=min, ...)
105+
return FilterWrapper.adapt(LevelRangeFilter.createFilter(max, min, onMatch,
99106
org.apache.logging.log4j.core.Filter.Result.DENY));
100107
}
101108
}
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache license, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the license for the specific language governing permissions and
15+
* limitations under the license.
16+
*/
17+
package org.apache.log4j.builders.filter;
18+
19+
import static org.junit.jupiter.api.Assertions.*;
20+
21+
import java.io.StringReader;
22+
import java.util.Properties;
23+
import java.util.stream.Stream;
24+
25+
import javax.xml.parsers.DocumentBuilderFactory;
26+
27+
import org.apache.log4j.bridge.FilterWrapper;
28+
import org.apache.log4j.spi.Filter;
29+
import org.apache.logging.log4j.Level;
30+
import org.apache.logging.log4j.core.Filter.Result;
31+
import org.apache.logging.log4j.core.filter.LevelRangeFilter;
32+
import org.junit.jupiter.api.extension.ExtensionContext;
33+
import org.junit.jupiter.params.ParameterizedTest;
34+
import org.junit.jupiter.params.provider.Arguments;
35+
import org.junit.jupiter.params.provider.ArgumentsProvider;
36+
import org.junit.jupiter.params.provider.ArgumentsSource;
37+
import org.w3c.dom.Element;
38+
import org.xml.sax.InputSource;
39+
40+
public class LevelRangeFilterBuilderTest {
41+
42+
@ParameterizedTest
43+
@ArgumentsSource(TestLevelRangeFilterBuilderProvider.class)
44+
public void testAcceptOnMatchTrue(TestLevelRangeFilterBuilder builder) throws Exception {
45+
LevelRangeFilter levelRangeFilter = builder.build(Level.INFO, Level.ERROR, true);
46+
47+
assertResult(Result.DENY, levelRangeFilter, Level.ALL);
48+
assertResult(Result.DENY, levelRangeFilter, Level.DEBUG);
49+
assertResult(Result.ACCEPT, levelRangeFilter, Level.INFO);
50+
assertResult(Result.ACCEPT, levelRangeFilter, Level.WARN);
51+
assertResult(Result.ACCEPT, levelRangeFilter, Level.ERROR);
52+
assertResult(Result.DENY, levelRangeFilter, Level.FATAL);
53+
assertResult(Result.DENY, levelRangeFilter, Level.OFF);
54+
}
55+
56+
@ParameterizedTest
57+
@ArgumentsSource(TestLevelRangeFilterBuilderProvider.class)
58+
public void testAcceptOnMatchFalse(TestLevelRangeFilterBuilder builder) throws Exception {
59+
LevelRangeFilter levelRangeFilter = builder.build(Level.INFO, Level.ERROR, false);
60+
61+
assertResult(Result.DENY, levelRangeFilter, Level.ALL);
62+
assertResult(Result.DENY, levelRangeFilter, Level.DEBUG);
63+
assertResult(Result.NEUTRAL, levelRangeFilter, Level.INFO);
64+
assertResult(Result.NEUTRAL, levelRangeFilter, Level.WARN);
65+
assertResult(Result.NEUTRAL, levelRangeFilter, Level.ERROR);
66+
assertResult(Result.DENY, levelRangeFilter, Level.FATAL);
67+
assertResult(Result.DENY, levelRangeFilter, Level.OFF);
68+
}
69+
70+
@ParameterizedTest
71+
@ArgumentsSource(TestLevelRangeFilterBuilderProvider.class)
72+
public void testAcceptOnMatchNull(TestLevelRangeFilterBuilder builder) throws Exception {
73+
LevelRangeFilter levelRangeFilter = builder.build(Level.INFO, Level.ERROR, null);
74+
75+
assertResult(Result.DENY, levelRangeFilter, Level.ALL);
76+
assertResult(Result.DENY, levelRangeFilter, Level.DEBUG);
77+
assertResult(Result.NEUTRAL, levelRangeFilter, Level.INFO);
78+
assertResult(Result.NEUTRAL, levelRangeFilter, Level.WARN);
79+
assertResult(Result.NEUTRAL, levelRangeFilter, Level.ERROR);
80+
assertResult(Result.DENY, levelRangeFilter, Level.FATAL);
81+
assertResult(Result.DENY, levelRangeFilter, Level.OFF);
82+
}
83+
84+
@ParameterizedTest
85+
@ArgumentsSource(TestLevelRangeFilterBuilderProvider.class)
86+
public void testMinLevelNull(TestLevelRangeFilterBuilder builder) throws Exception {
87+
LevelRangeFilter levelRangeFilter = builder.build(null, Level.ERROR, true);
88+
89+
assertResult(Result.ACCEPT, levelRangeFilter, Level.ALL);
90+
assertResult(Result.ACCEPT, levelRangeFilter, Level.DEBUG);
91+
assertResult(Result.ACCEPT, levelRangeFilter, Level.INFO);
92+
assertResult(Result.ACCEPT, levelRangeFilter, Level.WARN);
93+
assertResult(Result.ACCEPT, levelRangeFilter, Level.ERROR);
94+
assertResult(Result.DENY, levelRangeFilter, Level.FATAL);
95+
assertResult(Result.DENY, levelRangeFilter, Level.OFF);
96+
}
97+
98+
@ParameterizedTest
99+
@ArgumentsSource(TestLevelRangeFilterBuilderProvider.class)
100+
public void testMaxLevelNull(TestLevelRangeFilterBuilder builder) throws Exception {
101+
LevelRangeFilter levelRangeFilter = builder.build(Level.INFO, null, true);
102+
103+
assertResult(Result.DENY, levelRangeFilter, Level.ALL);
104+
assertResult(Result.DENY, levelRangeFilter, Level.DEBUG);
105+
assertResult(Result.ACCEPT, levelRangeFilter, Level.INFO);
106+
assertResult(Result.ACCEPT, levelRangeFilter, Level.WARN);
107+
assertResult(Result.ACCEPT, levelRangeFilter, Level.ERROR);
108+
assertResult(Result.ACCEPT, levelRangeFilter, Level.FATAL);
109+
assertResult(Result.ACCEPT, levelRangeFilter, Level.OFF);
110+
}
111+
112+
@ParameterizedTest
113+
@ArgumentsSource(TestLevelRangeFilterBuilderProvider.class)
114+
public void testMinMaxLevelSame(TestLevelRangeFilterBuilder builder) throws Exception {
115+
LevelRangeFilter levelRangeFilter = builder.build(Level.INFO, Level.INFO, true);
116+
117+
assertResult(Result.DENY, levelRangeFilter, Level.ALL);
118+
assertResult(Result.DENY, levelRangeFilter, Level.DEBUG);
119+
assertResult(Result.ACCEPT, levelRangeFilter, Level.INFO);
120+
assertResult(Result.DENY, levelRangeFilter, Level.WARN);
121+
assertResult(Result.DENY, levelRangeFilter, Level.ERROR);
122+
assertResult(Result.DENY, levelRangeFilter, Level.FATAL);
123+
assertResult(Result.DENY, levelRangeFilter, Level.OFF);
124+
}
125+
126+
@ParameterizedTest
127+
@ArgumentsSource(TestLevelRangeFilterBuilderProvider.class)
128+
public void testMinMaxLevelNull(TestLevelRangeFilterBuilder builder) throws Exception {
129+
LevelRangeFilter levelRangeFilter = builder.build(null, null, true);
130+
131+
assertResult(Result.ACCEPT, levelRangeFilter, Level.ALL);
132+
assertResult(Result.ACCEPT, levelRangeFilter, Level.DEBUG);
133+
assertResult(Result.ACCEPT, levelRangeFilter, Level.INFO);
134+
assertResult(Result.ACCEPT, levelRangeFilter, Level.WARN);
135+
assertResult(Result.ACCEPT, levelRangeFilter, Level.ERROR);
136+
assertResult(Result.ACCEPT, levelRangeFilter, Level.FATAL);
137+
assertResult(Result.ACCEPT, levelRangeFilter, Level.OFF);
138+
}
139+
140+
private static void assertResult(Result expected, LevelRangeFilter filter, Level level) {
141+
assertSame(expected, filter.filter(null, level, null, (Object) null, null));
142+
}
143+
144+
private static class TestLevelRangeFilterBuilderProvider implements ArgumentsProvider {
145+
146+
@Override
147+
public Stream<? extends Arguments> provideArguments(ExtensionContext extensionContext) {
148+
return Stream.of(
149+
Arguments.of(new TestLevelRangeFilterFromXmlBuilder()),
150+
Arguments.of(new TestLevelRangeFilterFromPropertyBuilder())
151+
);
152+
}
153+
}
154+
155+
private interface TestLevelRangeFilterBuilder {
156+
157+
LevelRangeFilter build(Level levelMin, Level levelMax, Boolean acceptOnMatch) throws Exception;
158+
}
159+
160+
private static class TestLevelRangeFilterFromXmlBuilder implements TestLevelRangeFilterBuilder {
161+
162+
@Override
163+
public LevelRangeFilter build(Level levelMin, Level levelMax, Boolean acceptOnMatch) throws Exception {
164+
LevelRangeFilterBuilder builder = new LevelRangeFilterBuilder();
165+
Filter filter = builder.parse(generateTestXml(levelMin, levelMax, acceptOnMatch), null);
166+
org.apache.logging.log4j.core.Filter wrappedFilter = ((FilterWrapper) filter).getFilter();
167+
return (LevelRangeFilter) wrappedFilter;
168+
}
169+
170+
private static Element generateTestXml(Level levelMin, Level levelMax, Boolean acceptOnMatch) throws Exception {
171+
172+
StringBuilder sb = new StringBuilder();
173+
sb.append("<filter class=\"org.apache.log4j.varia.LevelRangeFilter\">\n");
174+
if (levelMin != null) {
175+
sb.append(String.format("<param name=\"LevelMin\" value=\"%s\"/>\n", levelMin));
176+
}
177+
if (levelMax != null) {
178+
sb.append(String.format("<param name=\"LevelMax\" value=\"%s\"/>\n", levelMax));
179+
}
180+
if (acceptOnMatch != null) {
181+
sb.append(String.format("<param name=\"AcceptOnMatch\" value=\"%b\"/>\n", acceptOnMatch));
182+
}
183+
sb.append("</filter>");
184+
185+
return DocumentBuilderFactory.newInstance().newDocumentBuilder()
186+
.parse(new InputSource(new StringReader(sb.toString())))
187+
.getDocumentElement();
188+
}
189+
}
190+
191+
private static class TestLevelRangeFilterFromPropertyBuilder implements TestLevelRangeFilterBuilder {
192+
193+
@Override
194+
public LevelRangeFilter build(Level levelMin, Level levelMax, Boolean acceptOnMatch) {
195+
Properties properties = new Properties();
196+
if (levelMin != null) {
197+
properties.setProperty("foobar.levelMin", levelMin.name());
198+
}
199+
if (levelMax != null) {
200+
properties.setProperty("foobar.levelMax", levelMax.name());
201+
}
202+
if (acceptOnMatch != null) {
203+
properties.setProperty("foobar.acceptOnMatch", acceptOnMatch.toString());
204+
}
205+
LevelRangeFilterBuilder builder = new LevelRangeFilterBuilder("foobar", properties);
206+
Filter filter = builder.parse(null);
207+
org.apache.logging.log4j.core.Filter wrappedFilter = ((FilterWrapper) filter).getFilter();
208+
return (LevelRangeFilter) wrappedFilter;
209+
}
210+
}
211+
}

log4j-1.2-api/src/test/java/org/apache/log4j/config/AbstractLog4j1ConfigurationTest.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,4 +611,33 @@ protected void testEnhancedRollingFileAppender(Configuration configuration) {
611611
assertEquals(11, defaultRolloverStrategy.getMinIndex());
612612
assertEquals(20, defaultRolloverStrategy.getMaxIndex());
613613
}
614+
615+
protected void testLevelRangeFilter() throws Exception {
616+
try (final LoggerContext ctx = configure("config-1.2/log4j-LevelRangeFilter")) {
617+
final Configuration config = ctx.getConfiguration();
618+
final Logger logger = LogManager.getLogger(PropertiesConfigurationTest.class);
619+
// List appender
620+
final Appender appender = config.getAppender("LIST");
621+
assertNotNull(appender);
622+
final ListAppender legacyAppender = (ListAppender) ((Adapter) appender).getAppender();
623+
// deny
624+
logger.trace("TRACE");
625+
assertEquals(0, legacyAppender.getEvents().size());
626+
// deny
627+
logger.debug("DEBUG");
628+
assertEquals(0, legacyAppender.getEvents().size());
629+
// accept
630+
logger.info("INFO");
631+
assertEquals(1, legacyAppender.getEvents().size());
632+
// accept
633+
logger.warn("WARN");
634+
assertEquals(2, legacyAppender.getEvents().size());
635+
// accept
636+
logger.error("ERROR");
637+
assertEquals(3, legacyAppender.getEvents().size());
638+
// deny
639+
logger.fatal("FATAL");
640+
assertEquals(3, legacyAppender.getEvents().size());
641+
}
642+
}
614643
}

log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesConfigurationTest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,4 +326,9 @@ public void testEnhancedRollingFileAppender() throws Exception {
326326
}
327327
}
328328

329+
@Override
330+
@Test
331+
public void testLevelRangeFilter() throws Exception {
332+
super.testLevelRangeFilter();
333+
}
329334
}

log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlConfigurationTest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,4 +202,9 @@ public void testEnhancedRollingFileAppender() throws Exception {
202202
}
203203
}
204204

205+
@Override
206+
@Test
207+
public void testLevelRangeFilter() throws Exception {
208+
super.testLevelRangeFilter();
209+
}
205210
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#
2+
# Licensed to the Apache Software Foundation (ASF) under one or more
3+
# contributor license agreements. See the NOTICE file distributed with
4+
# this work for additional information regarding copyright ownership.
5+
# The ASF licenses this file to You under the Apache License, Version 2.0
6+
# (the "License"); you may not use this file except in compliance with
7+
# the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
#
17+
18+
log4j.appender.LIST = org.apache.log4j.ListAppender
19+
log4j.appender.LIST.filter.1=org.apache.log4j.varia.LevelRangeFilter
20+
log4j.appender.LIST.filter.1.LevelMin=INFO
21+
log4j.appender.LIST.filter.1.LevelMax=ERROR
22+
log4j.appender.LIST.filter.1.AcceptOnMatch=false
23+
log4j.rootLogger = debug, LIST
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
Licensed to the Apache Software Foundation (ASF) under one or more
4+
contributor license agreements. See the NOTICE file distributed with
5+
this work for additional information regarding copyright ownership.
6+
The ASF licenses this file to You under the Apache License, Version 2.0
7+
(the "License"); you may not use this file except in compliance with
8+
the License. You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing, software
13+
distributed under the License is distributed on an "AS IS" BASIS,
14+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
See the License for the specific language governing permissions and
16+
limitations under the License.
17+
-->
18+
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
19+
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
20+
<appender name="LIST" class="org.apache.log4j.ListAppender">
21+
<filter class="org.apache.log4j.varia.LevelRangeFilter">
22+
<param name="levelMin" value="INFO"/>
23+
<param name="levelMax" value="ERROR"/>
24+
<param name="AcceptOnMatch" value="false"/>
25+
</filter>
26+
</appender>
27+
28+
<root>
29+
<priority value="debug"/>
30+
<appender-ref ref="LIST"/>
31+
</root>
32+
</log4j:configuration>

0 commit comments

Comments
 (0)