[ 
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)

Reply via email to