4242import org .apache .logging .log4j .core .lookup .StrSubstitutor ;
4343import org .apache .logging .log4j .core .util .KeyValuePair ;
4444import org .apache .logging .log4j .core .util .StringBuilderWriter ;
45- import org .apache .logging .log4j .message .MapMessage ;
4645import org .apache .logging .log4j .message .Message ;
46+ import org .apache .logging .log4j .message .MultiformatMessage ;
47+ import org .apache .logging .log4j .util .MultiFormatStringBuilderFormattable ;
4748import org .apache .logging .log4j .util .StringBuilderFormattable ;
4849import org .apache .logging .log4j .util .TriConsumer ;
4950
5556import java .util .HashSet ;
5657import java .util .List ;
5758import java .util .Set ;
59+ import java .util .concurrent .ConcurrentHashMap ;
60+ import java .util .concurrent .ConcurrentMap ;
5861
5962@ Plugin (name = "EcsLayout" , category = Node .CATEGORY , elementType = Layout .ELEMENT_TYPE )
6063public class EcsLayout extends AbstractStringLayout {
6164
6265 private static final ThreadLocal <StringBuilder > messageStringBuilder = new ThreadLocal <StringBuilder >();
6366 public static final Charset UTF_8 = Charset .forName ("UTF-8" );
67+ public static final String [] JSON_FORMAT = {"JSON" };
6468
6569 private final TriConsumer <String , Object , StringBuilder > WRITE_KEY_VALUES_INTO = new TriConsumer <String , Object , StringBuilder >() {
6670 @ Override
@@ -80,6 +84,7 @@ public void accept(final String key, final Object value, final StringBuilder str
8084 private final Set <String > topLevelLabels ;
8185 private String serviceName ;
8286 private boolean includeMarkers ;
87+ private final ConcurrentMap <Class <? extends MultiformatMessage >, Boolean > supportsJson = new ConcurrentHashMap <Class <? extends MultiformatMessage >, Boolean >();
8388
8489 private EcsLayout (Configuration config , String serviceName , boolean includeMarkers , KeyValuePair [] additionalFields , Collection <String > topLevelLabels ) {
8590 super (config , UTF_8 , null , null );
@@ -201,6 +206,37 @@ private void serializeMarker(StringBuilder builder, Marker marker) {
201206 }
202207
203208 private void serializeMessage (StringBuilder builder , boolean gcFree , Message message , Throwable thrown ) {
209+ if (message instanceof MultiformatMessage ) {
210+ MultiformatMessage multiformatMessage = (MultiformatMessage ) message ;
211+ if (supportsJson (multiformatMessage )) {
212+ serializeJsonMessage (builder , multiformatMessage );
213+ } else {
214+ serializeSimpleMessage (builder , gcFree , message , thrown );
215+ }
216+ } else {
217+ serializeSimpleMessage (builder , gcFree , message , thrown );
218+ }
219+ }
220+
221+ private void serializeJsonMessage (StringBuilder builder , MultiformatMessage message ) {
222+ final StringBuilder messageBuffer = getMessageStringBuilder ();
223+ if (message instanceof MultiFormatStringBuilderFormattable ) {
224+ ((MultiFormatStringBuilderFormattable ) message ).formatTo (JSON_FORMAT , messageBuffer );
225+ } else {
226+ messageBuffer .append (message .getFormattedMessage (JSON_FORMAT ));
227+ }
228+ if (isObject (messageBuffer )) {
229+ moveToRoot (messageBuffer );
230+ builder .append (messageBuffer );
231+ builder .append (", " );
232+ } else {
233+ builder .append ("\" message\" :" );
234+ builder .append (messageBuffer );
235+ builder .append (", " );
236+ }
237+ }
238+
239+ private void serializeSimpleMessage (StringBuilder builder , boolean gcFree , Message message , Throwable thrown ) {
204240 builder .append ("\" message\" :\" " );
205241 if (message instanceof CharSequence ) {
206242 JsonUtils .quoteAsString (((CharSequence ) message ), builder );
@@ -220,10 +256,30 @@ private void serializeMessage(StringBuilder builder, boolean gcFree, Message mes
220256 JsonUtils .quoteAsString (formatThrowable (thrown ), builder );
221257 }
222258 builder .append ("\" , " );
223- if (message instanceof MapMessage ) {
224- MapMessage mapMessage = (MapMessage ) message ;
225- mapMessage .forEach (WRITE_KEY_VALUES_INTO , builder );
259+ }
260+
261+ private boolean isObject (StringBuilder messageBuffer ) {
262+ return messageBuffer .length () > 1 && messageBuffer .charAt (0 ) == '{' && messageBuffer .charAt (messageBuffer .length () -1 ) == '}' ;
263+ }
264+
265+ private void moveToRoot (StringBuilder messageBuffer ) {
266+ messageBuffer .setCharAt (0 , ' ' );
267+ messageBuffer .setCharAt (messageBuffer .length () -1 , ' ' );
268+ }
269+
270+ private boolean supportsJson (MultiformatMessage message ) {
271+ Boolean supportsJson = this .supportsJson .get (message .getClass ());
272+ if (supportsJson == null ) {
273+ supportsJson = false ;
274+ for (String format : message .getFormats ()) {
275+ if (format .equalsIgnoreCase ("JSON" )) {
276+ supportsJson = true ;
277+ break ;
278+ }
279+ }
280+ this .supportsJson .put (message .getClass (), supportsJson );
226281 }
282+ return supportsJson ;
227283 }
228284
229285 private static CharSequence formatThrowable (final Throwable throwable ) {
0 commit comments