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