Print stacktrace with Log4j2 in JSON with JSONLayout in a single line Print stacktrace with Log4j2 in JSON with JSONLayout in a single line json json

Print stacktrace with Log4j2 in JSON with JSONLayout in a single line


<JsonLayout compact="true" eventEol="true" stacktraceAsString="true" >

This will help.


I solved this using a custom Log4j layout:

@Plugin(name = "FlatJsonLayout", category = "Core", elementType = "layout", printObject = true)public class FlatJsonLayout extends AbstractStringLayout {    private final ObjectMapper objectMapper;    FlatJsonLayout(Charset charset) {        super(charset);        SimpleModule module = new SimpleModule();        module.addSerializer(LogEvent.class, new LogEventSerializer());        module.addSerializer(Throwable.class, new ThrowableSerializer());        module.addSerializer(ReadOnlyStringMap.class, new ContextDataSerializer() {        });        objectMapper = new ObjectMapper();        objectMapper.registerModule(module);        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);    }    @Override    public String toSerializable(LogEvent logEvent) {        try {            return objectMapper.writeValueAsString(logEvent);        } catch (JsonProcessingException e) {            throw new IllegalStateException(e);        }    }    private static class LogEventSerializer extends StdSerializer<LogEvent> {        LogEventSerializer() {            super(LogEvent.class);        }        @Override        public void serialize(LogEvent value, JsonGenerator gen, SerializerProvider provider) throws IOException {            gen.writeStartObject();            gen.writeNumberField("timeMillis", value.getTimeMillis());            gen.writeStringField("threadName", value.getThreadName());            gen.writeStringField("level", value.getLevel().name());            gen.writeStringField("loggerName", value.getLoggerName());            gen.writeObjectField("marker", value.getMarker());            gen.writeObjectField("message", value.getMessage().getFormattedMessage());            gen.writeObjectField("thrown", value.getThrown());            gen.writeObjectField("ContextMap", value.getContextData());            gen.writeObjectField("ContextStack", value.getContextStack());            gen.writeStringField("loggerFQCN", value.getLoggerFqcn());            gen.writeObjectField("Source", value.getSource());            gen.writeBooleanField("endOfBatch", value.isEndOfBatch());            gen.writeEndObject();        }    }    private static class ThrowableSerializer extends StdSerializer<Throwable> {        ThrowableSerializer() {            super(Throwable.class);        }        @Override        public void serialize(Throwable value, JsonGenerator gen, SerializerProvider provider) throws IOException {            try (StringWriter stringWriter = new StringWriter()) {                try (PrintWriter printWriter = new PrintWriter(stringWriter)) {                    value.printStackTrace(printWriter);                    gen.writeString(stringWriter.toString());                }            } catch (IOException e) {                throw new IllegalStateException(e);            }        }    }    @PluginBuilderFactory    public static <B extends Builder<B>> B newBuilder() {        return new Builder<B>().asBuilder();    }    public static class Builder<B extends Builder<B>> extends org.apache.logging.log4j.core.layout.AbstractStringLayout.Builder<B> implements org.apache.logging.log4j.core.util.Builder<FlatJsonLayout> {        Builder() {            this.setCharset(StandardCharsets.UTF_8);        }        public FlatJsonLayout build() {            return new FlatJsonLayout(this.getCharset());        }    }}

The class has to be on the classpath of the application. It can then be configured in the log4j2.xml like this:

<?xml version="1.0" encoding="UTF-8"?><Configuration status="info">    <Appenders>        <Console name="Console" target="SYSTEM_OUT">            <FlatJsonLayout/>        </Console>    </Appenders>    <Loggers>        <Root level="info">            <AppenderRef ref="Console"/>        </Root>    </Loggers></Configuration>


On properties files appender.console.layout.complete is boolean not string so change:

appender.console.layout.complete="false"

to:

appender.console.layout.complete = false