Skip to content

Commit ad173a8

Browse files
yueki1993ppkarwasz
authored andcommitted
[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.
1 parent e5b0cc8 commit ad173a8

File tree

9 files changed

+351
-19
lines changed

9 files changed

+351
-19
lines changed

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

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public Filter parse(final Element filterElement, final XmlConfiguration config)
6666
levelMax.set(getValueAttribute(currentElement));
6767
break;
6868
case LEVEL_MIN:
69-
levelMax.set(getValueAttribute(currentElement));
69+
levelMin.set(getValueAttribute(currentElement));
7070
break;
7171
case ACCEPT_ON_MATCH:
7272
acceptOnMatch.set(getBooleanValueAttribute(currentElement));
@@ -86,21 +86,23 @@ public Filter parse(final PropertiesConfiguration config) {
8686
}
8787

8888
private Filter createFilter(final String levelMax, final String levelMin, final boolean acceptOnMatch) {
89-
Level max = Level.FATAL;
90-
Level min = Level.TRACE;
89+
Level max = Level.OFF;
90+
Level min = Level.ALL;
9191
if (levelMax != null) {
92-
max = OptionConverter.toLevel(levelMax, org.apache.log4j.Level.FATAL)
93-
.getVersion2Level();
92+
max = OptionConverter.toLevel(levelMax, org.apache.log4j.Level.OFF).getVersion2Level();
9493
}
9594
if (levelMin != null) {
96-
min = OptionConverter.toLevel(levelMin, org.apache.log4j.Level.DEBUG)
97-
.getVersion2Level();
95+
min = OptionConverter.toLevel(levelMin, org.apache.log4j.Level.ALL).getVersion2Level();
9896
}
9997
final org.apache.logging.log4j.core.Filter.Result onMatch = acceptOnMatch
10098
? org.apache.logging.log4j.core.Filter.Result.ACCEPT
10199
: org.apache.logging.log4j.core.Filter.Result.NEUTRAL;
102100

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, ...)
103105
return FilterWrapper.adapt(
104-
LevelRangeFilter.createFilter(min, max, onMatch, org.apache.logging.log4j.core.Filter.Result.DENY));
106+
LevelRangeFilter.createFilter(max, min, onMatch, org.apache.logging.log4j.core.Filter.Result.DENY));
105107
}
106108
}
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
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+
import javax.xml.parsers.DocumentBuilderFactory;
25+
import org.apache.log4j.bridge.FilterWrapper;
26+
import org.apache.log4j.spi.Filter;
27+
import org.apache.logging.log4j.Level;
28+
import org.apache.logging.log4j.core.Filter.Result;
29+
import org.apache.logging.log4j.core.filter.LevelRangeFilter;
30+
import org.junit.jupiter.api.extension.ExtensionContext;
31+
import org.junit.jupiter.params.ParameterizedTest;
32+
import org.junit.jupiter.params.provider.Arguments;
33+
import org.junit.jupiter.params.provider.ArgumentsProvider;
34+
import org.junit.jupiter.params.provider.ArgumentsSource;
35+
import org.w3c.dom.Element;
36+
import org.xml.sax.InputSource;
37+
38+
public class LevelRangeFilterBuilderTest {
39+
40+
@ParameterizedTest
41+
@ArgumentsSource(TestLevelRangeFilterBuilderProvider.class)
42+
public void testAcceptOnMatchTrue(TestLevelRangeFilterBuilder builder) throws Exception {
43+
LevelRangeFilter levelRangeFilter = builder.build(Level.INFO, Level.ERROR, true);
44+
45+
assertResult(Result.DENY, levelRangeFilter, Level.ALL);
46+
assertResult(Result.DENY, levelRangeFilter, Level.DEBUG);
47+
assertResult(Result.ACCEPT, levelRangeFilter, Level.INFO);
48+
assertResult(Result.ACCEPT, levelRangeFilter, Level.WARN);
49+
assertResult(Result.ACCEPT, levelRangeFilter, Level.ERROR);
50+
assertResult(Result.DENY, levelRangeFilter, Level.FATAL);
51+
assertResult(Result.DENY, levelRangeFilter, Level.OFF);
52+
}
53+
54+
@ParameterizedTest
55+
@ArgumentsSource(TestLevelRangeFilterBuilderProvider.class)
56+
public void testAcceptOnMatchFalse(TestLevelRangeFilterBuilder builder) throws Exception {
57+
LevelRangeFilter levelRangeFilter = builder.build(Level.INFO, Level.ERROR, false);
58+
59+
assertResult(Result.DENY, levelRangeFilter, Level.ALL);
60+
assertResult(Result.DENY, levelRangeFilter, Level.DEBUG);
61+
assertResult(Result.NEUTRAL, levelRangeFilter, Level.INFO);
62+
assertResult(Result.NEUTRAL, levelRangeFilter, Level.WARN);
63+
assertResult(Result.NEUTRAL, levelRangeFilter, Level.ERROR);
64+
assertResult(Result.DENY, levelRangeFilter, Level.FATAL);
65+
assertResult(Result.DENY, levelRangeFilter, Level.OFF);
66+
}
67+
68+
@ParameterizedTest
69+
@ArgumentsSource(TestLevelRangeFilterBuilderProvider.class)
70+
public void testAcceptOnMatchNull(TestLevelRangeFilterBuilder builder) throws Exception {
71+
LevelRangeFilter levelRangeFilter = builder.build(Level.INFO, Level.ERROR, null);
72+
73+
assertResult(Result.DENY, levelRangeFilter, Level.ALL);
74+
assertResult(Result.DENY, levelRangeFilter, Level.DEBUG);
75+
assertResult(Result.NEUTRAL, levelRangeFilter, Level.INFO);
76+
assertResult(Result.NEUTRAL, levelRangeFilter, Level.WARN);
77+
assertResult(Result.NEUTRAL, levelRangeFilter, Level.ERROR);
78+
assertResult(Result.DENY, levelRangeFilter, Level.FATAL);
79+
assertResult(Result.DENY, levelRangeFilter, Level.OFF);
80+
}
81+
82+
@ParameterizedTest
83+
@ArgumentsSource(TestLevelRangeFilterBuilderProvider.class)
84+
public void testMinLevelNull(TestLevelRangeFilterBuilder builder) throws Exception {
85+
LevelRangeFilter levelRangeFilter = builder.build(null, Level.ERROR, true);
86+
87+
assertResult(Result.ACCEPT, levelRangeFilter, Level.ALL);
88+
assertResult(Result.ACCEPT, levelRangeFilter, Level.DEBUG);
89+
assertResult(Result.ACCEPT, levelRangeFilter, Level.INFO);
90+
assertResult(Result.ACCEPT, levelRangeFilter, Level.WARN);
91+
assertResult(Result.ACCEPT, levelRangeFilter, Level.ERROR);
92+
assertResult(Result.DENY, levelRangeFilter, Level.FATAL);
93+
assertResult(Result.DENY, levelRangeFilter, Level.OFF);
94+
}
95+
96+
@ParameterizedTest
97+
@ArgumentsSource(TestLevelRangeFilterBuilderProvider.class)
98+
public void testMaxLevelNull(TestLevelRangeFilterBuilder builder) throws Exception {
99+
LevelRangeFilter levelRangeFilter = builder.build(Level.INFO, null, true);
100+
101+
assertResult(Result.DENY, levelRangeFilter, Level.ALL);
102+
assertResult(Result.DENY, levelRangeFilter, Level.DEBUG);
103+
assertResult(Result.ACCEPT, levelRangeFilter, Level.INFO);
104+
assertResult(Result.ACCEPT, levelRangeFilter, Level.WARN);
105+
assertResult(Result.ACCEPT, levelRangeFilter, Level.ERROR);
106+
assertResult(Result.ACCEPT, levelRangeFilter, Level.FATAL);
107+
assertResult(Result.ACCEPT, levelRangeFilter, Level.OFF);
108+
}
109+
110+
@ParameterizedTest
111+
@ArgumentsSource(TestLevelRangeFilterBuilderProvider.class)
112+
public void testMinMaxLevelSame(TestLevelRangeFilterBuilder builder) throws Exception {
113+
LevelRangeFilter levelRangeFilter = builder.build(Level.INFO, Level.INFO, true);
114+
115+
assertResult(Result.DENY, levelRangeFilter, Level.ALL);
116+
assertResult(Result.DENY, levelRangeFilter, Level.DEBUG);
117+
assertResult(Result.ACCEPT, levelRangeFilter, Level.INFO);
118+
assertResult(Result.DENY, levelRangeFilter, Level.WARN);
119+
assertResult(Result.DENY, levelRangeFilter, Level.ERROR);
120+
assertResult(Result.DENY, levelRangeFilter, Level.FATAL);
121+
assertResult(Result.DENY, levelRangeFilter, Level.OFF);
122+
}
123+
124+
@ParameterizedTest
125+
@ArgumentsSource(TestLevelRangeFilterBuilderProvider.class)
126+
public void testMinMaxLevelNull(TestLevelRangeFilterBuilder builder) throws Exception {
127+
LevelRangeFilter levelRangeFilter = builder.build(null, null, true);
128+
129+
assertResult(Result.ACCEPT, levelRangeFilter, Level.ALL);
130+
assertResult(Result.ACCEPT, levelRangeFilter, Level.DEBUG);
131+
assertResult(Result.ACCEPT, levelRangeFilter, Level.INFO);
132+
assertResult(Result.ACCEPT, levelRangeFilter, Level.WARN);
133+
assertResult(Result.ACCEPT, levelRangeFilter, Level.ERROR);
134+
assertResult(Result.ACCEPT, levelRangeFilter, Level.FATAL);
135+
assertResult(Result.ACCEPT, levelRangeFilter, Level.OFF);
136+
}
137+
138+
private static void assertResult(Result expected, LevelRangeFilter filter, Level level) {
139+
assertSame(expected, filter.filter(null, level, null, (Object) null, null));
140+
}
141+
142+
private static class TestLevelRangeFilterBuilderProvider implements ArgumentsProvider {
143+
144+
@Override
145+
public Stream<? extends Arguments> provideArguments(ExtensionContext extensionContext) {
146+
return Stream.of(
147+
Arguments.of(new TestLevelRangeFilterFromXmlBuilder()),
148+
Arguments.of(new TestLevelRangeFilterFromPropertyBuilder()));
149+
}
150+
}
151+
152+
private interface TestLevelRangeFilterBuilder {
153+
154+
LevelRangeFilter build(Level levelMin, Level levelMax, Boolean acceptOnMatch) throws Exception;
155+
}
156+
157+
private static class TestLevelRangeFilterFromXmlBuilder implements TestLevelRangeFilterBuilder {
158+
159+
@Override
160+
public LevelRangeFilter build(Level levelMin, Level levelMax, Boolean acceptOnMatch) throws Exception {
161+
LevelRangeFilterBuilder builder = new LevelRangeFilterBuilder();
162+
Filter filter = builder.parse(generateTestXml(levelMin, levelMax, acceptOnMatch), null);
163+
org.apache.logging.log4j.core.Filter wrappedFilter = ((FilterWrapper) filter).getFilter();
164+
return (LevelRangeFilter) wrappedFilter;
165+
}
166+
167+
private static Element generateTestXml(Level levelMin, Level levelMax, Boolean acceptOnMatch) throws Exception {
168+
169+
StringBuilder sb = new StringBuilder();
170+
sb.append("<filter class=\"org.apache.log4j.varia.LevelRangeFilter\">\n");
171+
if (levelMin != null) {
172+
sb.append(String.format("<param name=\"LevelMin\" value=\"%s\"/>\n", levelMin));
173+
}
174+
if (levelMax != null) {
175+
sb.append(String.format("<param name=\"LevelMax\" value=\"%s\"/>\n", levelMax));
176+
}
177+
if (acceptOnMatch != null) {
178+
sb.append(String.format("<param name=\"AcceptOnMatch\" value=\"%b\"/>\n", acceptOnMatch));
179+
}
180+
sb.append("</filter>");
181+
182+
return DocumentBuilderFactory.newInstance()
183+
.newDocumentBuilder()
184+
.parse(new InputSource(new StringReader(sb.toString())))
185+
.getDocumentElement();
186+
}
187+
}
188+
189+
private static class TestLevelRangeFilterFromPropertyBuilder implements TestLevelRangeFilterBuilder {
190+
191+
@Override
192+
public LevelRangeFilter build(Level levelMin, Level levelMax, Boolean acceptOnMatch) {
193+
Properties properties = new Properties();
194+
if (levelMin != null) {
195+
properties.setProperty("foobar.levelMin", levelMin.name());
196+
}
197+
if (levelMax != null) {
198+
properties.setProperty("foobar.levelMax", levelMax.name());
199+
}
200+
if (acceptOnMatch != null) {
201+
properties.setProperty("foobar.acceptOnMatch", acceptOnMatch.toString());
202+
}
203+
LevelRangeFilterBuilder builder = new LevelRangeFilterBuilder("foobar", properties);
204+
Filter filter = builder.parse(null);
205+
org.apache.logging.log4j.core.Filter wrappedFilter = ((FilterWrapper) filter).getFilter();
206+
return (LevelRangeFilter) wrappedFilter;
207+
}
208+
}
209+
}

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
@@ -625,4 +625,33 @@ protected void testEnhancedRollingFileAppender(final Configuration configuration
625625
assertEquals(11, defaultRolloverStrategy.getMinIndex());
626626
assertEquals(20, defaultRolloverStrategy.getMaxIndex());
627627
}
628+
629+
protected void testLevelRangeFilter() throws Exception {
630+
try (final LoggerContext ctx = configure("config-1.2/log4j-LevelRangeFilter")) {
631+
final Configuration config = ctx.getConfiguration();
632+
final Logger logger = LogManager.getLogger(PropertiesConfigurationTest.class);
633+
// List appender
634+
final Appender appender = config.getAppender("LIST");
635+
assertNotNull(appender);
636+
final ListAppender legacyAppender = (ListAppender) ((Adapter) appender).getAppender();
637+
// deny
638+
logger.trace("TRACE");
639+
assertEquals(0, legacyAppender.getEvents().size());
640+
// deny
641+
logger.debug("DEBUG");
642+
assertEquals(0, legacyAppender.getEvents().size());
643+
// accept
644+
logger.info("INFO");
645+
assertEquals(1, legacyAppender.getEvents().size());
646+
// accept
647+
logger.warn("WARN");
648+
assertEquals(2, legacyAppender.getEvents().size());
649+
// accept
650+
logger.error("ERROR");
651+
assertEquals(3, legacyAppender.getEvents().size());
652+
// deny
653+
logger.fatal("FATAL");
654+
assertEquals(3, legacyAppender.getEvents().size());
655+
}
656+
}
628657
}

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

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,38 @@ public void testConsoleAppenderLevelRangeFilter() throws Exception {
127127
final Filterable filterable = (Filterable) appender;
128128
final CompositeFilter filter = (CompositeFilter) filterable.getFilter();
129129
final org.apache.logging.log4j.core.Filter[] filters = filter.getFiltersArray();
130-
final LevelRangeFilter customFilterReal = (LevelRangeFilter) filters[0];
131-
assertEquals(Level.ALL, customFilterReal.getMinLevel());
132-
final LevelRangeFilter defaultFilter = (LevelRangeFilter) filters[1];
133-
assertEquals(Level.TRACE, defaultFilter.getMinLevel());
130+
final LevelRangeFilter filter1 = (LevelRangeFilter) filters[0];
131+
// XXX: LOG4J2-2315
132+
assertEquals(Level.OFF, filter1.getMinLevel());
133+
assertEquals(Level.ALL, filter1.getMaxLevel());
134+
final LevelRangeFilter filter2 = (LevelRangeFilter) filters[1];
135+
assertEquals(Level.ERROR, filter2.getMinLevel());
136+
assertEquals(Level.INFO, filter2.getMaxLevel());
137+
final LevelRangeFilter filter3 = (LevelRangeFilter) filters[2];
138+
assertEquals(Level.OFF, filter3.getMinLevel());
139+
assertEquals(Level.ALL, filter3.getMaxLevel());
140+
141+
final ListAppender legacyAppender = (ListAppender) ((AppenderAdapter.Adapter) appender).getAppender();
142+
final Logger logger = LogManager.getLogger(PropertiesConfigurationTest.class);
143+
144+
// deny
145+
logger.trace("TRACE");
146+
assertEquals(0, legacyAppender.getEvents().size());
147+
// deny
148+
logger.debug("DEBUG");
149+
assertEquals(0, legacyAppender.getEvents().size());
150+
// accept
151+
logger.info("INFO");
152+
assertEquals(1, legacyAppender.getEvents().size());
153+
// accept
154+
logger.warn("WARN");
155+
assertEquals(2, legacyAppender.getEvents().size());
156+
// accept
157+
logger.error("ERROR");
158+
assertEquals(3, legacyAppender.getEvents().size());
159+
// deny
160+
logger.fatal("FATAL");
161+
assertEquals(3, legacyAppender.getEvents().size());
134162
}
135163
}
136164

@@ -330,4 +358,10 @@ public void testEnhancedRollingFileAppender() throws Exception {
330358
testEnhancedRollingFileAppender(configuration);
331359
}
332360
}
361+
362+
@Override
363+
@Test
364+
public void testLevelRangeFilter() throws Exception {
365+
super.testLevelRangeFilter();
366+
}
333367
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,4 +204,10 @@ public void testEnhancedRollingFileAppender() throws Exception {
204204
assertTrue(policies[1] instanceof SizeBasedTriggeringPolicy, "is SizeBasedTriggeringPolicy");
205205
}
206206
}
207+
208+
@Override
209+
@Test
210+
public void testLevelRangeFilter() throws Exception {
211+
super.testLevelRangeFilter();
212+
}
207213
}

log4j-1.2-api/src/test/resources/LOG4J2-3326.properties

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,12 @@
1515
# limitations under the License.
1616
#
1717

18-
log4j.appender.CUSTOM=org.apache.log4j.CustomNoopAppender
18+
log4j.appender.CUSTOM=org.apache.log4j.ListAppender
1919
log4j.appender.CUSTOM.filter.1=org.apache.log4j.varia.LevelRangeFilter
2020
log4j.appender.CUSTOM.filter.1.levelMin=ALL
2121
log4j.appender.CUSTOM.filter.2=org.apache.log4j.varia.LevelRangeFilter
22+
log4j.appender.CUSTOM.filter.2.levelMin=INFO
23+
log4j.appender.CUSTOM.filter.2.levelMax=ERROR
24+
log4j.appender.CUSTOM.filter.3=org.apache.log4j.varia.LevelRangeFilter
2225

2326
log4j.rootLogger=trace, CUSTOM
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

0 commit comments

Comments
 (0)