You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by ma...@apache.org on 2022/01/17 20:04:09 UTC
[logging-log4j2] 01/02: [LOG4J2-3343] Add excludePattern to JTL maps
This is an automated email from the ASF dual-hosted git repository.
mattsicker pushed a commit to branch release-2.x
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git
commit c7906fb9b4ac3f518d2b4256bff3e11422b21fc6
Author: Matt Sicker <ma...@apache.org>
AuthorDate: Mon Jan 17 14:03:15 2022 -0600
[LOG4J2-3343] Add excludePattern to JTL maps
This adds a new option to exclude keys from map-based resolvers using a regular expression.
Signed-off-by: Matt Sicker <ma...@apache.org>
---
.../json/resolver/ReadOnlyStringMapResolver.java | 47 +++++++++++++++-------
.../resolver/ReadOnlyStringMapResolverTest.java | 26 ++++++++++++
.../asciidoc/manual/json-template-layout.adoc.vm | 26 +++++++-----
3 files changed, 74 insertions(+), 25 deletions(-)
diff --git a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/ReadOnlyStringMapResolver.java b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/ReadOnlyStringMapResolver.java
index 788fa5d..65abfb0 100644
--- a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/ReadOnlyStringMapResolver.java
+++ b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/ReadOnlyStringMapResolver.java
@@ -34,18 +34,19 @@ import java.util.regex.Pattern;
* <h3>Configuration</h3>
*
* <pre>
- * config = singleAccess | multiAccess
+ * config = singleAccess | multiAccess
*
- * singleAccess = key , [ stringified ]
- * key = "key" -> string
- * stringified = "stringified" -> boolean
+ * singleAccess = key , [ stringified ]
+ * key = "key" -> string
+ * stringified = "stringified" -> boolean
*
- * multiAccess = [ pattern ] , [ replacement ] , [ flatten ] , [ stringified ]
- * pattern = "pattern" -> string
- * replacement = "replacement" -> string
- * flatten = "flatten" -> ( boolean | flattenConfig )
- * flattenConfig = [ flattenPrefix ]
- * flattenPrefix = "prefix" -> string
+ * multiAccess = [ pattern ] , [ replacement ] , [ excludePattern ] , [ flatten ] , [ stringified ]
+ * pattern = "pattern" -> string
+ * excludePattern = "excludePattern" -> string
+ * replacement = "replacement" -> string
+ * flatten = "flatten" -> ( boolean | flattenConfig )
+ * flattenConfig = [ flattenPrefix ]
+ * flattenPrefix = "prefix" -> string
* </pre>
*
* Note that <tt>singleAccess</tt> resolves a single field, whilst
@@ -61,14 +62,17 @@ import java.util.regex.Pattern;
* These two are effectively equivalent to
* <tt>Pattern.compile(pattern).matcher(key).matches()</tt> and
* <tt>Pattern.compile(pattern).matcher(key).replaceAll(replacement)</tt> calls.
+ * Regex provided in the <tt>excludePattern</tt> is used to exclude matching keys.
+ * This exclusion pattern is not used with the previously mentioned
+ * <tt>replacement</tt> option.
*
* <h3>Garbage Footprint</h3>
*
* <tt>stringified</tt> allocates a new <tt>String</tt> for values that are not
* of type <tt>String</tt>.
* <p>
- * <tt>pattern</tt> and <tt>replacement</tt> incur pattern matcher allocation
- * costs.
+ * <tt>pattern</tt>, <tt>excludePattern</tt> and <tt>replacement</tt> incur pattern
+ * matcher allocation costs.
* <p>
* Writing certain non-primitive values (e.g., <tt>BigDecimal</tt>,
* <tt>Set</tt>, etc.) to JSON generates garbage, though most (e.g.,
@@ -208,6 +212,11 @@ class ReadOnlyStringMapResolver implements EventResolver {
throw new IllegalArgumentException(
"replacement cannot be provided without a pattern: " + config);
}
+ final String excludePattern = config.getString("excludePattern");
+ if (excludePattern != null && key != null) {
+ throw new IllegalArgumentException(
+ "excludePattern and key options cannot be combined: " + config);
+ }
final boolean stringified = config.getBoolean("stringified", false);
if (key != null) {
return createKeyResolver(key, stringified, mapAccessor);
@@ -219,7 +228,7 @@ class ReadOnlyStringMapResolver implements EventResolver {
prefix,
pattern,
replacement,
- stringified,
+ excludePattern, stringified,
mapAccessor);
}
}
@@ -257,6 +266,7 @@ class ReadOnlyStringMapResolver implements EventResolver {
final String prefix,
final String pattern,
final String replacement,
+ final String excludePattern,
final boolean stringified,
final Function<LogEvent, ReadOnlyStringMap> mapAccessor) {
@@ -265,6 +275,8 @@ class ReadOnlyStringMapResolver implements EventResolver {
pattern == null
? null
: Pattern.compile(pattern);
+ final Pattern exclusionPattern =
+ excludePattern == null ? null : Pattern.compile(excludePattern);
// Create the recycler for the loop context.
final Recycler<LoopContext> loopContextRecycler =
@@ -276,6 +288,7 @@ class ReadOnlyStringMapResolver implements EventResolver {
}
loopContext.pattern = compiledPattern;
loopContext.replacement = replacement;
+ loopContext.exclusionPattern = exclusionPattern;
loopContext.stringified = stringified;
return loopContext;
});
@@ -354,6 +367,8 @@ class ReadOnlyStringMapResolver implements EventResolver {
private String replacement;
+ private Pattern exclusionPattern;
+
private boolean stringified;
private JsonWriter jsonWriter;
@@ -376,7 +391,11 @@ class ReadOnlyStringMapResolver implements EventResolver {
final Matcher matcher = loopContext.pattern != null
? loopContext.pattern.matcher(key)
: null;
- final boolean keyMatched = matcher == null || matcher.matches();
+ final Matcher mismatcher = loopContext.exclusionPattern != null
+ ? loopContext.exclusionPattern.matcher(key)
+ : null;
+ final boolean keyMatched = (matcher == null || matcher.matches())
+ && (mismatcher == null || !mismatcher.matches());
if (keyMatched) {
final String replacedKey =
matcher != null && loopContext.replacement != null
diff --git a/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/ReadOnlyStringMapResolverTest.java b/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/ReadOnlyStringMapResolverTest.java
index 921e153..34fd944 100644
--- a/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/ReadOnlyStringMapResolverTest.java
+++ b/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/ReadOnlyStringMapResolverTest.java
@@ -29,6 +29,7 @@ import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.List;
+import java.util.Map;
import java.util.regex.PatternSyntaxException;
import static org.apache.logging.log4j.layout.template.json.TestHelpers.*;
@@ -450,4 +451,29 @@ class ReadOnlyStringMapResolverTest {
}
+ @Test
+ void test_map_exclude_pattern() {
+ final StringMapMessage message = new StringMapMessage()
+ .with("first", "alpha")
+ .with("second", "beta")
+ .with("third", "gamma")
+ .with("fourth", "delta");
+ final LogEvent logEvent = Log4jLogEvent.newBuilder()
+ .setMessage(message)
+ .build();
+
+ final String eventTemplate = writeJson(asMap(
+ "map", asMap(
+ "$resolver", "map",
+ "excludePattern", ".+d")));
+
+ final JsonTemplateLayout layout = JsonTemplateLayout.newBuilder()
+ .setConfiguration(CONFIGURATION)
+ .setEventTemplate(eventTemplate)
+ .build();
+
+ usingSerializedLogEventAccessor(layout, logEvent,
+ accessor -> assertThat(accessor.getObject("map")).isInstanceOfSatisfying(Map.class,
+ map -> assertThat(map).containsOnlyKeys("first", "fourth")));
+ }
}
diff --git a/src/site/asciidoc/manual/json-template-layout.adoc.vm b/src/site/asciidoc/manual/json-template-layout.adoc.vm
index 53410b2..a7b6a61 100644
--- a/src/site/asciidoc/manual/json-template-layout.adoc.vm
+++ b/src/site/asciidoc/manual/json-template-layout.adoc.vm
@@ -1346,18 +1346,19 @@ and garbage footprint, which are detailed below.
[source]
----
-config = singleAccess | multiAccess
+config = singleAccess | multiAccess
-singleAccess = key , [ stringified ]
-key = "key" -> string
-stringified = "stringified" -> boolean
+singleAccess = key , [ stringified ]
+key = "key" -> string
+stringified = "stringified" -> boolean
-multiAccess = [ pattern ] , [ replacement ] , [ flatten ] , [ stringified ]
-pattern = "pattern" -> string
-replacement = "replacement" -> string
-flatten = "flatten" -> ( boolean | flattenConfig )
-flattenConfig = [ flattenPrefix ]
-flattenPrefix = "prefix" -> string
+multiAccess = [ pattern ] , [ replacement ] , [ excludePattern ] , [ flatten ] , [ stringified ]
+pattern = "pattern" -> string
+replacement = "replacement" -> string
+excludePattern = "excludePattern" -> string
+flatten = "flatten" -> ( boolean | flattenConfig )
+flattenConfig = [ flattenPrefix ]
+flattenPrefix = "prefix" -> string
----
`singleAccess` resolves a single field, whilst `multiAccess` resolves a
@@ -1370,13 +1371,16 @@ Regex provided in the `pattern` is used to match against the keys. If provided,
`replacement` will be used to replace the matched keys. These two are
effectively equivalent to `Pattern.compile(pattern).matcher(key).matches()` and
`Pattern.compile(pattern).matcher(key).replaceAll(replacement)` calls.
+Regex provided in the `excludePattern` is used to exclude matching keys.
+This exclusion pattern is not used with the previously mentioned `replacement`
+option.
[WARNING]
====
Regarding garbage footprint, `stringified` flag translates to
`String.valueOf(value)`, hence mind not-`String`-typed values.
-`pattern` and `replacement` incur pattern matcher allocation costs.
+`pattern`, `excludePattern`, and `replacement` incur pattern matcher allocation costs.
Writing certain non-primitive values (e.g., `BigDecimal`, `Set`, etc.) to JSON
generates garbage, though most (e.g., `int`, `long`, `String`, `List`,