Skip to content

Set the default minLevel and maxLevel of LevelRangeFilter #1503

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,41 +17,77 @@
package org.apache.logging.log4j.core.filter;

import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.Filter.Result;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.impl.Log4jLogEvent;
import org.apache.logging.log4j.message.SimpleMessage;
import org.assertj.core.api.SoftAssertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;

import static org.junit.jupiter.api.Assertions.*;
import static org.apache.logging.log4j.core.filter.LevelRangeFilter.createFilter;
import static org.assertj.core.api.Assertions.assertThat;

public class LevelRangeFilterTest {
class LevelRangeFilterTest {

@Test
public void testLevels() {
final LevelRangeFilter filter = LevelRangeFilter.createFilter(Level.ERROR, Level.INFO, null, null);
filter.start();
assertTrue(filter.isStarted());
assertSame(Filter.Result.DENY, filter.filter(null, Level.DEBUG, null, (Object) null, (Throwable) null));
assertSame(Filter.Result.NEUTRAL, filter.filter(null, Level.ERROR, null, (Object) null, (Throwable) null));
LogEvent event = Log4jLogEvent.newBuilder() //
.setLevel(Level.DEBUG) //
.setMessage(new SimpleMessage("Test")) //
.build();
assertSame(Filter.Result.DENY, filter.filter(event));
event = Log4jLogEvent.newBuilder() //
.setLevel(Level.ERROR) //
.setMessage(new SimpleMessage("Test")) //
.build();
assertSame(Filter.Result.NEUTRAL, filter.filter(event));
void verify_constants() {
assertThat(LevelRangeFilter.DEFAULT_MIN_LEVEL).isEqualTo(Level.OFF);
assertThat(LevelRangeFilter.DEFAULT_MAX_LEVEL).isEqualTo(Level.ALL);
assertThat(LevelRangeFilter.DEFAULT_ON_MATCH).isEqualTo(Result.NEUTRAL);
assertThat(LevelRangeFilter.DEFAULT_ON_MISMATCH).isEqualTo(Result.DENY);
}

@Test
void verify_defaults() {
final LevelRangeFilter filter = createFilter(null, null, null, null);
assertThat(filter.getMinLevel()).isEqualTo(Level.OFF);
assertThat(filter.getMaxLevel()).isEqualTo(Level.ALL);
assertThat(filter.getOnMatch()).isEqualTo(Result.NEUTRAL);
assertThat(filter.getOnMismatch()).isEqualTo(Result.DENY);
}

@ParameterizedTest
@MethodSource("org.apache.logging.log4j.Level#values")
void default_should_match_all_levels(final Level level) {
final LevelRangeFilter filter = createFilter(null, null, null, null);
assertThat(filter.filter(createEvent(level))).isEqualTo(LevelRangeFilter.DEFAULT_ON_MATCH);
}

@Test
public void testMinimumOnlyLevel() {
final LevelRangeFilter filter = LevelRangeFilter.createFilter(Level.ERROR, null, null, null);
filter.start();
assertTrue(filter.isStarted());
assertSame(Filter.Result.NEUTRAL, filter.filter(null, Level.ERROR, null, (Object) null, (Throwable) null));
void overriding_defaults_should_be_effective() {

// Choose a configuration
final Level minLevel = Level.ERROR;
final Level maxLevel = Level.WARN;
final Result onMatch = Result.ACCEPT;
final Result onMismatch = Result.NEUTRAL;

// Verify we deviate from the defaults
assertThat(minLevel).isNotEqualTo(LevelRangeFilter.DEFAULT_MIN_LEVEL);
assertThat(maxLevel).isNotEqualTo(LevelRangeFilter.DEFAULT_MAX_LEVEL);
assertThat(onMatch).isNotEqualTo(LevelRangeFilter.DEFAULT_ON_MATCH);
assertThat(onMismatch).isNotEqualTo(LevelRangeFilter.DEFAULT_ON_MISMATCH);

// Verify the filtering
final LevelRangeFilter filter = createFilter(minLevel, maxLevel, onMatch, onMismatch);
final SoftAssertions assertions = new SoftAssertions();
for (final Level level : Level.values()) {
final Result expectedResult = level.isInRange(minLevel, maxLevel) ? onMatch : onMismatch;
assertions.assertThat(filter.filter(createEvent(level))).isEqualTo(expectedResult);
}
assertions.assertAll();

}

private static LogEvent createEvent(final Level level) {
final SimpleMessage message = new SimpleMessage("test message at level " + level);
return Log4jLogEvent
.newBuilder()
.setLevel(level)
.setMessage(message)
.build();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -29,47 +29,62 @@
import org.apache.logging.log4j.util.PerformanceSensitive;

/**
* This filter returns the {@code onMatch} result if the level in the {@code LogEvent} is in the range of the configured
* min and max levels, otherwise it returns {@code onMismatch} value . For example, if the filter is configured with
* {@link Level#ERROR} and {@link Level#INFO} and the LogEvent contains {@link Level#WARN} then the onMatch value will
* be returned since {@link Level#WARN WARN} events are in between {@link Level#ERROR ERROR} and {@link Level#INFO
* INFO}.
* This filter returns the {@link #onMatch} result if the level of the {@link LogEvent} is in the range of the configured {@link #minLevel} and {@link #maxLevel} values, otherwise it returns the {@link #onMismatch} result.
* The default values for {@link #minLevel} and {@link #maxLevel} are set to {@link Level#OFF} and {@link Level#ALL}, respectively.
* The default values for {@link #onMatch} and {@link #onMismatch} are set to {@link Result#NEUTRAL} and {@link Result#DENY}, respectively.
* <p>
* The default Levels are both {@link Level#ERROR ERROR}.
* The levels get compared by their associated integral values; {@link Level#OFF} has an integral value of 0, {@link Level#FATAL} 100, {@link Level#ERROR} 200, and so on.
* For example, if the filter is configured with {@link #maxLevel} set to {@link Level#INFO}, the filter will return {@link #onMismatch} result for {@link LogEvent}s of level with higher integral values; {@link Level#DEBUG}, {@link Level#TRACE}, etc.
* </p>
*/
@Plugin(name = "LevelRangeFilter", category = Node.CATEGORY, elementType = Filter.ELEMENT_TYPE, printObject = true)
@PerformanceSensitive("allocation")
public final class LevelRangeFilter extends AbstractFilter {

/**
* Creates a ThresholdFilter.
* The default minimum level threshold.
*/
public static final Level DEFAULT_MIN_LEVEL = Level.OFF;

/**
* THe default maximum level threshold.
*/
public static final Level DEFAULT_MAX_LEVEL = Level.ALL;

/**
* The default result on a match.
*/
public static final Result DEFAULT_ON_MATCH = Result.NEUTRAL;

/**
* The default result on a mismatch.
*/
public static final Result DEFAULT_ON_MISMATCH = Result.DENY;

/**
* Creates an instance with the provided properties.
*
* @param minLevel
* The minimum log Level.
* @param maxLevel
* The maximum log Level.
* @param match
* The action to take on a match.
* @param mismatch
* The action to take on a mismatch.
* @return The created ThresholdFilter.
* @param minLevel the minimum level threshold
* @param maxLevel the maximum level threshold
* @param onMatch the result to return on a match
* @param onMismatch the result to return on a mismatch
* @return a new instance
*/
// TODO Consider refactoring to use AbstractFilter.AbstractFilterBuilder
@PluginFactory
public static LevelRangeFilter createFilter(
// @formatter:off
@PluginAttribute("minLevel") final Level minLevel,
@PluginAttribute("maxLevel") final Level maxLevel,
@PluginAttribute("onMatch") final Result match,
@PluginAttribute("onMismatch") final Result mismatch) {
@PluginAttribute("onMatch") final Result onMatch,
@PluginAttribute("onMismatch") final Result onMismatch) {
Comment on lines +78 to +79
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might take this opportunity to switch to the builder pattern. AbstractFilter.AbstractFilterBuilder has already defaults for onMatch and onMismatch.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have indeed seen that TODO and checked it. Though the required LoC doubles. Hence, sticked to this version.

// @formatter:on
final Level actualMinLevel = minLevel == null ? Level.ERROR : minLevel;
final Level actualMaxLevel = maxLevel == null ? Level.ERROR : maxLevel;
final Result onMatch = match == null ? Result.NEUTRAL : match;
final Result onMismatch = mismatch == null ? Result.DENY : mismatch;
return new LevelRangeFilter(actualMinLevel, actualMaxLevel, onMatch, onMismatch);
final Level effectiveMinLevel = minLevel == null ? DEFAULT_MIN_LEVEL : minLevel;
final Level effectiveMaxLevel = maxLevel == null ? DEFAULT_MAX_LEVEL : maxLevel;
final Result effectiveOnMatch = onMatch == null ? DEFAULT_ON_MATCH : onMatch;
final Result effectiveOnMismatch = onMismatch == null ? DEFAULT_ON_MISMATCH : onMismatch;
return new LevelRangeFilter(effectiveMinLevel, effectiveMaxLevel, effectiveOnMatch, effectiveOnMismatch);
}

private final Level maxLevel;

private final Level minLevel;
Expand All @@ -81,7 +96,7 @@ private LevelRangeFilter(final Level minLevel, final Level maxLevel, final Resul
}

private Result filter(final Level level) {
return level.isInRange(this.minLevel, this.maxLevel) ? onMatch : onMismatch;
return level.isInRange(minLevel, maxLevel) ? onMatch : onMismatch;
}

@Override
Expand Down Expand Up @@ -176,17 +191,23 @@ public Result filter(final Logger logger, final Level level, final Marker marker
return filter(level);
}

/**
* @return the minimum level threshold
*/
public Level getMinLevel() {
return minLevel;
}

/**
* @return the maximum level threshold
*/
public Level getMaxLevel() {
return maxLevel;
}

@Override
public String toString() {
return minLevel.toString();
return String.format("[%s,%s]", minLevel, maxLevel);
}

}
25 changes: 25 additions & 0 deletions src/changelog/.2.x.x/1503_change_defaults_for_LevelRangeFilter.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?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.
-->
<entry xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://logging.apache.org/log4j/changelog"
xsi:schemaLocation="http://logging.apache.org/log4j/changelog https://logging.apache.org/log4j/changelog-0.1.1.xsd"
type="changed">
<issue id="1503" link="https://github.com/apache/logging-log4j2/pull/1503"/>
<author id="vy"/>
<description format="asciidoc">Set the default `minLevel` and `maxLevel` of `LevelRangeFilter` to `OFF` and `ALL`, respectively</description>
</entry>
54 changes: 54 additions & 0 deletions src/site/xdoc/manual/filters.xml
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,60 @@
</Loggers>
</Configuration>]]></pre>
</subsection>

<a name="LevelRangeFilter"/>
<subsection name="LevelRangeFilter">
<p>
<code>LevelRangeFilter</code> allows filtering against a level range, where levels get compared by their associated integral values; <code>OFF</code> has an integral value of 0, <code>FATAL</code> 100, <code>ERROR</code> 200, and so on.
</p>
<table>
<caption align="top">Map Filter Parameters</caption>
<tr>
<th>Parameter Name</th>
<th>Type</th>
<th>Description</th>
</tr>
<tr>
<td><code>minLevel</code></td>
<td><code>Level</code></td>
<td>the minimum level threshold (defaults to <code>OFF</code>, which has an integral value of 0)</td>
</tr>
<tr>
<td><code>maxLevel</code></td>
<td><code>Level</code></td>
<td>the maximum level threshold (defaults to <code>ALL</code>, which has an integral value of <code>Integer.MAX_VALUE</code>)</td>
</tr>
<tr>
<td><code>onMatch</code></td>
<td><code>Filter.Result</code></td>
<td>the result to return on a match, where allowed values are <code>ACCEPT</code>, <code>DENY</code> or <code>NEUTRAL</code> (default)</td>
</tr>
<tr>
<td><code>onMismatch</code></td>
<td><code>Filter.Result</code></td>
<td>the result to return on a mismatch, where allowed values are <code>ACCEPT</code>, <code>DENY</code> (default) or <code>NEUTRAL</code></td>
</tr>
</table>
<p>
In the following example configuration, a <code>LevelRangeFilter</code> is configured with <code>maxLevel</code> set to <code>INFO</code>.
The filter will return <code>onMismatch</code> result (i.e., <code>DENY</code>, the default) for log events of level with higher integral values than <code>INFO</code>; i.e., <code>DEBUG</code>, <code>TRACE</code>, etc.
</p>
<pre class="prettyprint linenums"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" name="MyApp">
<Appenders>
<Console name="STDOUT">
<LevelRangeFilter maxLevel="INFO"/>
<PatternLayout pattern="%d %p %c{1.} [%t] %m%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="ERROR">
<AppenderRef ref="STDOUT"/>
</Root>
</Loggers>
</Configuration>]]></pre>
</subsection>

<a name="MapFilter"/>
<subsection name="MapFilter">
<p>
Expand Down