[
https://issues.apache.org/jira/browse/LOG4J2-3074?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17338352#comment-17338352
]
Paul Rubin edited comment on LOG4J2-3074 at 5/3/21, 12:43 PM:
--------------------------------------------------------------
Here are the changes I made to
org.apache.logging.log4j.layout.template.json.resolver.ReadOnlyStringMapResolver.
This may ne be the best way to handle, but it demonstrates the needed
functionality
{code:java}
private static EventResolver createResolver(
final EventResolverContext context,
final TemplateResolverConfig config,
final Function<LogEvent, ReadOnlyStringMap> mapAccessor) {
final Object flattenObject = config.getObject("flatten");
final boolean flatten;
if (flattenObject == null) {
flatten = false;
} else if (flattenObject instanceof Boolean) {
flatten = (boolean) flattenObject;
} else if (flattenObject instanceof Map) {
flatten = true;
} else {
throw new IllegalArgumentException("invalid flatten option: " +
config);
}
final String key = config.getString("key");
final String prefix = config.getString(new String[] {"flatten",
"prefix"});
final String pattern = config.getString("pattern");
{color:red} final String replacement = config.getString("replacement");
{color} final boolean stringified = config.getBoolean("stringified",
false);
if (key != null) {
if (flatten) {
throw new IllegalArgumentException(
"both key and flatten options cannot be supplied: " +
config);
}
return createKeyResolver(key, stringified, mapAccessor);
} else {
final RecyclerFactory recyclerFactory =
context.getRecyclerFactory();
return createResolver(
recyclerFactory,
flatten,
prefix,
pattern,
{color:red} replacement,
{color} stringified,
mapAccessor);
}
}
private static EventResolver createResolver(
final RecyclerFactory recyclerFactory,
final boolean flatten,
final String prefix,
final String pattern,
{color:red} final String replacement,
{color} final boolean stringified,
final Function<LogEvent, ReadOnlyStringMap> mapAccessor) {
// Compile the pattern.
final Pattern compiledPattern =
pattern == null
? null
: Pattern.compile(pattern);
// Create the recycler for the loop context.
final Recycler<LoopContext> loopContextRecycler =
recyclerFactory.create(() -> {
final LoopContext loopContext = new LoopContext();
if (prefix != null) {
loopContext.prefix = prefix;
loopContext.prefixedKey = new StringBuilder(prefix);
}
loopContext.pattern = compiledPattern;
{color:red} loopContext.replacement = replacement;
{color} loopContext.stringified = stringified;
return loopContext;
});
// Create the resolver.
return createResolver(flatten, loopContextRecycler, mapAccessor);
}
private static final class LoopContext {
private String prefix;
private StringBuilder prefixedKey;
private Pattern pattern;
{color:red} private String replacement;
{color}
private boolean stringified;
private JsonWriter jsonWriter;
private int initJsonWriterStringBuilderLength;
private boolean succeedingEntry;
}
@Override
public void accept(
final String key,
final Object value,
final LoopContext loopContext) {
final boolean keyMatched =
loopContext.pattern == null ||
loopContext.pattern.matcher(key).matches();
if (keyMatched) {
String fixedKey = key;
{color:red} if (loopContext.replacement != null &&
!loopContext.replacement.isEmpty()) {
// assume char[0] = $
Matcher matcher = loopContext.pattern.matcher(key);
if (matcher.find()) {
fixedKey =
matcher.group(Integer.valueOf(loopContext.replacement.substring(1)));
}
}
{color} final boolean succeedingEntry =
loopContext.succeedingEntry ||
loopContext.initJsonWriterStringBuilderLength <
loopContext.jsonWriter.getStringBuilder().length();
if (succeedingEntry) {
loopContext.jsonWriter.writeSeparator();
}
if (loopContext.prefix == null) {
loopContext.jsonWriter.writeObjectKey(fixedKey);
} else {
loopContext.prefixedKey.setLength(loopContext.prefix.length());
loopContext.prefixedKey.append(fixedKey);
loopContext.jsonWriter.writeObjectKey(loopContext.prefixedKey);
}
if (loopContext.stringified && !(value instanceof String)) {
final String valueString = String.valueOf(value);
loopContext.jsonWriter.writeString(valueString);
} else {
loopContext.jsonWriter.writeValue(value);
}
}
}
}
{code}
was (Author: paulrubin37):
Here are the changes I made to
org.apache.logging.log4j.layout.template.json.resolver.ReadOnlyStringMapResolver
{code:java}
private static EventResolver createResolver(
final EventResolverContext context,
final TemplateResolverConfig config,
final Function<LogEvent, ReadOnlyStringMap> mapAccessor) {
final Object flattenObject = config.getObject("flatten");
final boolean flatten;
if (flattenObject == null) {
flatten = false;
} else if (flattenObject instanceof Boolean) {
flatten = (boolean) flattenObject;
} else if (flattenObject instanceof Map) {
flatten = true;
} else {
throw new IllegalArgumentException("invalid flatten option: " +
config);
}
final String key = config.getString("key");
final String prefix = config.getString(new String[] {"flatten",
"prefix"});
final String pattern = config.getString("pattern");
{color:red} final String replacement = config.getString("replacement");
{color} final boolean stringified = config.getBoolean("stringified",
false);
if (key != null) {
if (flatten) {
throw new IllegalArgumentException(
"both key and flatten options cannot be supplied: " +
config);
}
return createKeyResolver(key, stringified, mapAccessor);
} else {
final RecyclerFactory recyclerFactory =
context.getRecyclerFactory();
return createResolver(
recyclerFactory,
flatten,
prefix,
pattern,
{color:red} replacement,
{color} stringified,
mapAccessor);
}
}
private static EventResolver createResolver(
final RecyclerFactory recyclerFactory,
final boolean flatten,
final String prefix,
final String pattern,
{color:red} final String replacement,
{color} final boolean stringified,
final Function<LogEvent, ReadOnlyStringMap> mapAccessor) {
// Compile the pattern.
final Pattern compiledPattern =
pattern == null
? null
: Pattern.compile(pattern);
// Create the recycler for the loop context.
final Recycler<LoopContext> loopContextRecycler =
recyclerFactory.create(() -> {
final LoopContext loopContext = new LoopContext();
if (prefix != null) {
loopContext.prefix = prefix;
loopContext.prefixedKey = new StringBuilder(prefix);
}
loopContext.pattern = compiledPattern;
{color:red} loopContext.replacement = replacement;
{color} loopContext.stringified = stringified;
return loopContext;
});
// Create the resolver.
return createResolver(flatten, loopContextRecycler, mapAccessor);
}
private static final class LoopContext {
private String prefix;
private StringBuilder prefixedKey;
private Pattern pattern;
{color:red} private String replacement;
{color}
private boolean stringified;
private JsonWriter jsonWriter;
private int initJsonWriterStringBuilderLength;
private boolean succeedingEntry;
}
@Override
public void accept(
final String key,
final Object value,
final LoopContext loopContext) {
final boolean keyMatched =
loopContext.pattern == null ||
loopContext.pattern.matcher(key).matches();
if (keyMatched) {
String fixedKey = key;
{color:red} if (loopContext.replacement != null &&
!loopContext.replacement.isEmpty()) {
// assume char[0] = $
Matcher matcher = loopContext.pattern.matcher(key);
if (matcher.find()) {
fixedKey =
matcher.group(Integer.valueOf(loopContext.replacement.substring(1)));
}
}
{color} final boolean succeedingEntry =
loopContext.succeedingEntry ||
loopContext.initJsonWriterStringBuilderLength <
loopContext.jsonWriter.getStringBuilder().length();
if (succeedingEntry) {
loopContext.jsonWriter.writeSeparator();
}
if (loopContext.prefix == null) {
loopContext.jsonWriter.writeObjectKey(fixedKey);
} else {
loopContext.prefixedKey.setLength(loopContext.prefix.length());
loopContext.prefixedKey.append(fixedKey);
loopContext.jsonWriter.writeObjectKey(loopContext.prefixedKey);
}
if (loopContext.stringified && !(value instanceof String)) {
final String valueString = String.valueOf(value);
loopContext.jsonWriter.writeString(valueString);
} else {
loopContext.jsonWriter.writeValue(value);
}
}
}
}
{code}
> Add replacement parameter to mdc resolver
> -----------------------------------------
>
> Key: LOG4J2-3074
> URL: https://issues.apache.org/jira/browse/LOG4J2-3074
> Project: Log4j 2
> Issue Type: Improvement
> Components: JsonTemplateLayout
> Affects Versions: 2.14.1
> Reporter: Paul Rubin
> Priority: Minor
> Fix For: 2.15.0
>
>
> This story aims to add a {{replacement}} parameter to
> {{ReadOnlyStringMapResolver}} (which is used by {{ThreadContextDataResolver}}
> and {{MapResolver}}) for modifying the keys of the map before resolving them.
> That is, given the following JSON template
> {code:json}
> {
> "user": {
> "$resolver": "mdc",
> "pattern": "user:(.*)",
> "replacement": "$1"
> }
> }
> {code}
> A {{LogEvent}} with an MDC map composed of {{user:name}}, {{user:role}},
> {{sessionId}} entries will generate the following JSON:
> {code:json}
> {
> "user": {
> "name": "Paul Rubin",
> "role": "ADMIN"
> }
> }
> {code}
--
This message was sent by Atlassian Jira
(v8.3.4#803005)