[ 
https://issues.apache.org/jira/browse/LOG4J2-3298?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17472649#comment-17472649
 ] 

Volkan Yazici commented on LOG4J2-3298:
---------------------------------------

What you are asking for is already possible, you just need to create your own 
event template resolver. How to do this is [documented and explained in the 
manual|https://logging.apache.org/log4j/2.x/manual/json-template-layout.html#extending-event-resolvers].
 I will share the outline in a moment, but before that, let me remind you that 
_"serializing every data model"_ is not always a good idea. Most of the time 
JSON logs are persisted to a storage solution (e.g., Elasticsearch) that keeps 
a statically-typed index on fields. Hence, trying to log a model containing 
{{long userId}} and another model containing {{String userId}} might not be 
suitable for your storage solution.
 # You can introduce a custom message resolver leveraging Jackson to serialize 
payloads in {{{}ObjectMessage{}}}'s as follows:
{code:java}
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.layout.template.json.resolver.EventResolver;
import org.apache.logging.log4j.layout.template.json.util.JsonWriter;
import org.apache.logging.log4j.message.ObjectMessage;

final class JacksonMessageResolver implements EventResolver {

    private static final JacksonMessageResolver INSTANCE = new 
JacksonMessageResolver();

    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    static JacksonMessageResolver getInstance() {
        return INSTANCE;
    }

    static String getName() {
        return "jacksonMessage";
    }

    @Override
    public boolean isResolvable(LogEvent logEvent) {
        return logEvent.getMessage() instanceof ObjectMessage;
    }

    @Override
    public void resolve(LogEvent logEvent, JsonWriter jsonWriter) {
        ObjectMessage message = (ObjectMessage) logEvent.getMessage();
        Object value = message.getParameter();
        String valueJson;
        try {
            // With sufficient sorcery, this call can be made garbage-free.
            valueJson = OBJECT_MAPPER.writeValueAsString(value);
        } catch (JsonProcessingException error) {
            throw new RuntimeException(error);
        }
        jsonWriter.writeRawString(valueJson);
    }

}
{code}

 # Next you need to register this as a {{JsonTemplateLayout}} resolver:
{code:java}
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.apache.logging.log4j.layout.template.json.resolver.*;

@Plugin(name = "JacksonMessageResolverFactory", category = 
TemplateResolverFactory.CATEGORY)
public final class JacksonMessageResolverFactory implements 
EventResolverFactory {

    private static final JacksonMessageResolverFactory INSTANCE = new 
JacksonMessageResolverFactory();

    private JacksonMessageResolverFactory() {}

    @PluginFactory
    public static JacksonMessageResolverFactory getInstance() {
        return INSTANCE;
    }

    @Override
    public String getName() {
        return JacksonMessageResolver.getName();
    }

    @Override
    public EventResolver create(EventResolverContext context, 
TemplateResolverConfig config) {
        return JacksonMessageResolver.getInstance();
    }

}
{code}

 # Configure your event template to employ the new resolver:
{code:json}
{
  "message": {
    "$resolver": "jacksonMessage"
  } 
}
{code}

 # wrap your data models in an {{{}ObjectMessage{}}}:
{code:java}
logger.info(new ObjectMessage(shoppingOrder));
{code}

Does this help?

> Update JSONTemplateFormat to support not escaping certain payloads
> ------------------------------------------------------------------
>
>                 Key: LOG4J2-3298
>                 URL: https://issues.apache.org/jira/browse/LOG4J2-3298
>             Project: Log4j 2
>          Issue Type: Improvement
>          Components: JsonTemplateLayout
>            Reporter: Matt Pavlovich
>            Assignee: Volkan Yazici
>            Priority: Minor
>
> Currently, if a user's object is able to generate well-formed JSON string, it 
> will be escaped. It would be great if there was a way to config the 
> JsonTemplateLayout to _not escape_ certain payloads. This allows the json to 
> be fully formed and contain model object data that can be parsed out later 
> without any un-formatting. 
> Example log entry:
> {noformat}
> {"@timestamp":"2022-01-07T19:14:01.060Z","log.level":"INFO","message":"{ 
> \"Order\" { \"id\": \"3977\", \"customerName\": \"Customer-8625\", 
> \"createdDate\": \"2022-01-07T09:51:01.056580-06:00\"} 
> }","process.thread.name":"main","log.logger":"io.foo.log.ProcessService"}
> {noformat}
> Desired output:
> {noformat}
> {"@timestamp":"2022-01-07T19:14:01.060Z","log.level":"INFO","message":{ 
> "Order" { "id": "3977", "customerName": "Customer-8625", "createdDate": 
> "2022-01-07T09:51:01.056580-06:00"} 
> },"process.thread.name":"main","log.logger":"io.foo.log.ProcessService"}
> {noformat}
> Proposed requirements:
> 1. Users would have to pre-escape their JSON string in order to not break 
> overall log json format
> Implementation approach options:
> 1. Update JSONTemplate configuration to allow a rawJSON, similar to 
> 'stringified' 
> 2. Data is written down as a JSON object "message": { }
> 3. User should invoke using an ObjectMessage (or perhaps a 
> JSONFormatMessage?): logger.info(new ObjectMessage(objectInstance))
> {noformat}
> "message": {
>     "$resolver": "message",
>     "rawJSON": true
>   },
> {noformat}



--
This message was sent by Atlassian Jira
(v8.20.1#820001)

Reply via email to