View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.core.layout;
18  
19  import java.io.IOException;
20  import java.io.Writer;
21  import java.nio.charset.Charset;
22  import java.nio.charset.StandardCharsets;
23  import java.util.HashMap;
24  import java.util.Map;
25  
26  import org.apache.logging.log4j.core.Layout;
27  import org.apache.logging.log4j.core.LogEvent;
28  import org.apache.logging.log4j.core.config.Configuration;
29  import org.apache.logging.log4j.core.config.DefaultConfiguration;
30  import org.apache.logging.log4j.core.config.Node;
31  import org.apache.logging.log4j.core.config.plugins.Plugin;
32  import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
33  import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
34  import org.apache.logging.log4j.core.config.plugins.PluginFactory;
35  
36  /**
37   * Appends a series of JSON events as strings serialized as bytes.
38   *
39   * <h3>Complete well-formed JSON vs. fragment JSON</h3>
40   * <p>
41   * If you configure {@code complete="true"}, the appender outputs a well-formed JSON document. By default, with
42   * {@code complete="false"}, you should include the output as an <em>external file</em> in a separate file to form a
43   * well-formed JSON document.
44   * </p>
45   * <p>
46   * A well-formed JSON event follows this pattern:
47   * </p>
48   *
49   * <pre>
50   * {
51    "timeMillis": 1,
52    "thread": "MyThreadName",
53    "level": "DEBUG",
54    "loggerName": "a.B",
55    "marker": {
56      "name": "Marker1",
57      "parents": [{
58        "name": "ParentMarker1",
59        "parents": [{
60          "name": "GrandMotherMarker"
61        }, {
62          "name": "GrandFatherMarker"
63        }]
64      }, {
65        "name": "GrandFatherMarker"
66      }]
67    },
68    "message": "Msg",
69    "thrown": {
70      "cause": {
71        "commonElementCount": 27,
72        "extendedStackTrace": [{
73          "class": "org.apache.logging.log4j.core.layout.LogEventFixtures",
74          "method": "createLogEvent",
75          "file": "LogEventFixtures.java",
76          "line": 53,
77          "exact": false,
78          "location": "test-classes/",
79          "version": "?"
80        }],
81        "localizedMessage": "testNPEx",
82        "message": "testNPEx",
83        "name": "java.lang.NullPointerException"
84      },
85      "commonElementCount": 0,
86      "extendedStackTrace": [{
87        "class": "org.apache.logging.log4j.core.layout.LogEventFixtures",
88        "method": "createLogEvent",
89        "file": "LogEventFixtures.java",
90        "line": 56,
91        "exact": true,
92        "location": "test-classes/",
93        "version": "?"
94      }, {
95        "class": "org.apache.logging.log4j.core.layout.JsonLayoutTest",
96        "method": "testAllFeatures",
97        "file": "JsonLayoutTest.java",
98        "line": 105,
99        "exact": true,
100       "location": "test-classes/",
101       "version": "?"
102     }, {
103       "class": "org.apache.logging.log4j.core.layout.JsonLayoutTest",
104       "method": "testLocationOnCompactOnMdcOn",
105       "file": "JsonLayoutTest.java",
106       "line": 268,
107       "exact": true,
108       "location": "test-classes/",
109       "version": "?"
110     }, {
111       "class": "sun.reflect.NativeMethodAccessorImpl",
112       "method": "invoke",
113       "line": -1,
114       "exact": false,
115       "location": "?",
116       "version": "1.7.0_55"
117     }, {
118       "class": "sun.reflect.NativeMethodAccessorImpl",
119       "method": "invoke",
120       "line": -1,
121       "exact": false,
122       "location": "?",
123       "version": "1.7.0_55"
124     }, {
125       "class": "sun.reflect.DelegatingMethodAccessorImpl",
126       "method": "invoke",
127       "line": -1,
128       "exact": false,
129       "location": "?",
130       "version": "1.7.0_55"
131     }, {
132       "class": "java.lang.reflect.Method",
133       "method": "invoke",
134       "line": -1,
135       "exact": false,
136       "location": "?",
137       "version": "1.7.0_55"
138     }, {
139       "class": "org.junit.runners.model.FrameworkMethod$1",
140       "method": "runReflectiveCall",
141       "file": "FrameworkMethod.java",
142       "line": 47,
143       "exact": true,
144       "location": "junit-4.11.jar",
145       "version": "?"
146     }, {
147       "class": "org.junit.internal.runners.model.ReflectiveCallable",
148       "method": "run",
149       "file": "ReflectiveCallable.java",
150       "line": 12,
151       "exact": true,
152       "location": "junit-4.11.jar",
153       "version": "?"
154     }, {
155       "class": "org.junit.runners.model.FrameworkMethod",
156       "method": "invokeExplosively",
157       "file": "FrameworkMethod.java",
158       "line": 44,
159       "exact": true,
160       "location": "junit-4.11.jar",
161       "version": "?"
162     }, {
163       "class": "org.junit.internal.runners.statements.InvokeMethod",
164       "method": "evaluate",
165       "file": "InvokeMethod.java",
166       "line": 17,
167       "exact": true,
168       "location": "junit-4.11.jar",
169       "version": "?"
170     }, {
171       "class": "org.junit.runners.ParentRunner",
172       "method": "runLeaf",
173       "file": "ParentRunner.java",
174       "line": 271,
175       "exact": true,
176       "location": "junit-4.11.jar",
177       "version": "?"
178     }, {
179       "class": "org.junit.runners.BlockJUnit4ClassRunner",
180       "method": "runChild",
181       "file": "BlockJUnit4ClassRunner.java",
182       "line": 70,
183       "exact": true,
184       "location": "junit-4.11.jar",
185       "version": "?"
186     }, {
187       "class": "org.junit.runners.BlockJUnit4ClassRunner",
188       "method": "runChild",
189       "file": "BlockJUnit4ClassRunner.java",
190       "line": 50,
191       "exact": true,
192       "location": "junit-4.11.jar",
193       "version": "?"
194     }, {
195       "class": "org.junit.runners.ParentRunner$3",
196       "method": "run",
197       "file": "ParentRunner.java",
198       "line": 238,
199       "exact": true,
200       "location": "junit-4.11.jar",
201       "version": "?"
202     }, {
203       "class": "org.junit.runners.ParentRunner$1",
204       "method": "schedule",
205       "file": "ParentRunner.java",
206       "line": 63,
207       "exact": true,
208       "location": "junit-4.11.jar",
209       "version": "?"
210     }, {
211       "class": "org.junit.runners.ParentRunner",
212       "method": "runChildren",
213       "file": "ParentRunner.java",
214       "line": 236,
215       "exact": true,
216       "location": "junit-4.11.jar",
217       "version": "?"
218     }, {
219       "class": "org.junit.runners.ParentRunner",
220       "method": "access$000",
221       "file": "ParentRunner.java",
222       "line": 53,
223       "exact": true,
224       "location": "junit-4.11.jar",
225       "version": "?"
226     }, {
227       "class": "org.junit.runners.ParentRunner$2",
228       "method": "evaluate",
229       "file": "ParentRunner.java",
230       "line": 229,
231       "exact": true,
232       "location": "junit-4.11.jar",
233       "version": "?"
234     }, {
235       "class": "org.junit.internal.runners.statements.RunBefores",
236       "method": "evaluate",
237       "file": "RunBefores.java",
238       "line": 26,
239       "exact": true,
240       "location": "junit-4.11.jar",
241       "version": "?"
242     }, {
243       "class": "org.junit.internal.runners.statements.RunAfters",
244       "method": "evaluate",
245       "file": "RunAfters.java",
246       "line": 27,
247       "exact": true,
248       "location": "junit-4.11.jar",
249       "version": "?"
250     }, {
251       "class": "org.junit.runners.ParentRunner",
252       "method": "run",
253       "file": "ParentRunner.java",
254       "line": 309,
255       "exact": true,
256       "location": "junit-4.11.jar",
257       "version": "?"
258     }, {
259       "class": "org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference",
260       "method": "run",
261       "file": "JUnit4TestReference.java",
262       "line": 50,
263       "exact": true,
264       "location": ".cp/",
265       "version": "?"
266     }, {
267       "class": "org.eclipse.jdt.internal.junit.runner.TestExecution",
268       "method": "run",
269       "file": "TestExecution.java",
270       "line": 38,
271       "exact": true,
272       "location": ".cp/",
273       "version": "?"
274     }, {
275       "class": "org.eclipse.jdt.internal.junit.runner.RemoteTestRunner",
276       "method": "runTests",
277       "file": "RemoteTestRunner.java",
278       "line": 467,
279       "exact": true,
280       "location": ".cp/",
281       "version": "?"
282     }, {
283       "class": "org.eclipse.jdt.internal.junit.runner.RemoteTestRunner",
284       "method": "runTests",
285       "file": "RemoteTestRunner.java",
286       "line": 683,
287       "exact": true,
288       "location": ".cp/",
289       "version": "?"
290     }, {
291       "class": "org.eclipse.jdt.internal.junit.runner.RemoteTestRunner",
292       "method": "run",
293       "file": "RemoteTestRunner.java",
294       "line": 390,
295       "exact": true,
296       "location": ".cp/",
297       "version": "?"
298     }, {
299       "class": "org.eclipse.jdt.internal.junit.runner.RemoteTestRunner",
300       "method": "main",
301       "file": "RemoteTestRunner.java",
302       "line": 197,
303       "exact": true,
304       "location": ".cp/",
305       "version": "?"
306     }],
307     "localizedMessage": "testIOEx",
308     "message": "testIOEx",
309     "name": "java.io.IOException",
310     "suppressed": [{
311       "commonElementCount": 0,
312       "extendedStackTrace": [{
313         "class": "org.apache.logging.log4j.core.layout.LogEventFixtures",
314         "method": "createLogEvent",
315         "file": "LogEventFixtures.java",
316         "line": 57,
317         "exact": true,
318         "location": "test-classes/",
319         "version": "?"
320       }, {
321         "class": "org.apache.logging.log4j.core.layout.JsonLayoutTest",
322         "method": "testAllFeatures",
323         "file": "JsonLayoutTest.java",
324         "line": 105,
325         "exact": true,
326         "location": "test-classes/",
327         "version": "?"
328       }, {
329         "class": "org.apache.logging.log4j.core.layout.JsonLayoutTest",
330         "method": "testLocationOnCompactOnMdcOn",
331         "file": "JsonLayoutTest.java",
332         "line": 268,
333         "exact": true,
334         "location": "test-classes/",
335         "version": "?"
336       }, {
337         "class": "sun.reflect.NativeMethodAccessorImpl",
338         "method": "invoke",
339         "line": -1,
340         "exact": false,
341         "location": "?",
342         "version": "1.7.0_55"
343       }, {
344         "class": "sun.reflect.NativeMethodAccessorImpl",
345         "method": "invoke",
346         "line": -1,
347         "exact": false,
348         "location": "?",
349         "version": "1.7.0_55"
350       }, {
351         "class": "sun.reflect.DelegatingMethodAccessorImpl",
352         "method": "invoke",
353         "line": -1,
354         "exact": false,
355         "location": "?",
356         "version": "1.7.0_55"
357       }, {
358         "class": "java.lang.reflect.Method",
359         "method": "invoke",
360         "line": -1,
361         "exact": false,
362         "location": "?",
363         "version": "1.7.0_55"
364       }, {
365         "class": "org.junit.runners.model.FrameworkMethod$1",
366         "method": "runReflectiveCall",
367         "file": "FrameworkMethod.java",
368         "line": 47,
369         "exact": true,
370         "location": "junit-4.11.jar",
371         "version": "?"
372       }, {
373         "class": "org.junit.internal.runners.model.ReflectiveCallable",
374         "method": "run",
375         "file": "ReflectiveCallable.java",
376         "line": 12,
377         "exact": true,
378         "location": "junit-4.11.jar",
379         "version": "?"
380       }, {
381         "class": "org.junit.runners.model.FrameworkMethod",
382         "method": "invokeExplosively",
383         "file": "FrameworkMethod.java",
384         "line": 44,
385         "exact": true,
386         "location": "junit-4.11.jar",
387         "version": "?"
388       }, {
389         "class": "org.junit.internal.runners.statements.InvokeMethod",
390         "method": "evaluate",
391         "file": "InvokeMethod.java",
392         "line": 17,
393         "exact": true,
394         "location": "junit-4.11.jar",
395         "version": "?"
396       }, {
397         "class": "org.junit.runners.ParentRunner",
398         "method": "runLeaf",
399         "file": "ParentRunner.java",
400         "line": 271,
401         "exact": true,
402         "location": "junit-4.11.jar",
403         "version": "?"
404       }, {
405         "class": "org.junit.runners.BlockJUnit4ClassRunner",
406         "method": "runChild",
407         "file": "BlockJUnit4ClassRunner.java",
408         "line": 70,
409         "exact": true,
410         "location": "junit-4.11.jar",
411         "version": "?"
412       }, {
413         "class": "org.junit.runners.BlockJUnit4ClassRunner",
414         "method": "runChild",
415         "file": "BlockJUnit4ClassRunner.java",
416         "line": 50,
417         "exact": true,
418         "location": "junit-4.11.jar",
419         "version": "?"
420       }, {
421         "class": "org.junit.runners.ParentRunner$3",
422         "method": "run",
423         "file": "ParentRunner.java",
424         "line": 238,
425         "exact": true,
426         "location": "junit-4.11.jar",
427         "version": "?"
428       }, {
429         "class": "org.junit.runners.ParentRunner$1",
430         "method": "schedule",
431         "file": "ParentRunner.java",
432         "line": 63,
433         "exact": true,
434         "location": "junit-4.11.jar",
435         "version": "?"
436       }, {
437         "class": "org.junit.runners.ParentRunner",
438         "method": "runChildren",
439         "file": "ParentRunner.java",
440         "line": 236,
441         "exact": true,
442         "location": "junit-4.11.jar",
443         "version": "?"
444       }, {
445         "class": "org.junit.runners.ParentRunner",
446         "method": "access$000",
447         "file": "ParentRunner.java",
448         "line": 53,
449         "exact": true,
450         "location": "junit-4.11.jar",
451         "version": "?"
452       }, {
453         "class": "org.junit.runners.ParentRunner$2",
454         "method": "evaluate",
455         "file": "ParentRunner.java",
456         "line": 229,
457         "exact": true,
458         "location": "junit-4.11.jar",
459         "version": "?"
460       }, {
461         "class": "org.junit.internal.runners.statements.RunBefores",
462         "method": "evaluate",
463         "file": "RunBefores.java",
464         "line": 26,
465         "exact": true,
466         "location": "junit-4.11.jar",
467         "version": "?"
468       }, {
469         "class": "org.junit.internal.runners.statements.RunAfters",
470         "method": "evaluate",
471         "file": "RunAfters.java",
472         "line": 27,
473         "exact": true,
474         "location": "junit-4.11.jar",
475         "version": "?"
476       }, {
477         "class": "org.junit.runners.ParentRunner",
478         "method": "run",
479         "file": "ParentRunner.java",
480         "line": 309,
481         "exact": true,
482         "location": "junit-4.11.jar",
483         "version": "?"
484       }, {
485         "class": "org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference",
486         "method": "run",
487         "file": "JUnit4TestReference.java",
488         "line": 50,
489         "exact": true,
490         "location": ".cp/",
491         "version": "?"
492       }, {
493         "class": "org.eclipse.jdt.internal.junit.runner.TestExecution",
494         "method": "run",
495         "file": "TestExecution.java",
496         "line": 38,
497         "exact": true,
498         "location": ".cp/",
499         "version": "?"
500       }, {
501         "class": "org.eclipse.jdt.internal.junit.runner.RemoteTestRunner",
502         "method": "runTests",
503         "file": "RemoteTestRunner.java",
504         "line": 467,
505         "exact": true,
506         "location": ".cp/",
507         "version": "?"
508       }, {
509         "class": "org.eclipse.jdt.internal.junit.runner.RemoteTestRunner",
510         "method": "runTests",
511         "file": "RemoteTestRunner.java",
512         "line": 683,
513         "exact": true,
514         "location": ".cp/",
515         "version": "?"
516       }, {
517         "class": "org.eclipse.jdt.internal.junit.runner.RemoteTestRunner",
518         "method": "run",
519         "file": "RemoteTestRunner.java",
520         "line": 390,
521         "exact": true,
522         "location": ".cp/",
523         "version": "?"
524       }, {
525         "class": "org.eclipse.jdt.internal.junit.runner.RemoteTestRunner",
526         "method": "main",
527         "file": "RemoteTestRunner.java",
528         "line": 197,
529         "exact": true,
530         "location": ".cp/",
531         "version": "?"
532       }],
533       "localizedMessage": "I am suppressed exception 1",
534       "message": "I am suppressed exception 1",
535       "name": "java.lang.IndexOutOfBoundsException"
536     }, {
537       "commonElementCount": 0,
538       "extendedStackTrace": [{
539         "class": "org.apache.logging.log4j.core.layout.LogEventFixtures",
540         "method": "createLogEvent",
541         "file": "LogEventFixtures.java",
542         "line": 58,
543         "exact": true,
544         "location": "test-classes/",
545         "version": "?"
546       }, {
547         "class": "org.apache.logging.log4j.core.layout.JsonLayoutTest",
548         "method": "testAllFeatures",
549         "file": "JsonLayoutTest.java",
550         "line": 105,
551         "exact": true,
552         "location": "test-classes/",
553         "version": "?"
554       }, {
555         "class": "org.apache.logging.log4j.core.layout.JsonLayoutTest",
556         "method": "testLocationOnCompactOnMdcOn",
557         "file": "JsonLayoutTest.java",
558         "line": 268,
559         "exact": true,
560         "location": "test-classes/",
561         "version": "?"
562       }, {
563         "class": "sun.reflect.NativeMethodAccessorImpl",
564         "method": "invoke",
565         "line": -1,
566         "exact": false,
567         "location": "?",
568         "version": "1.7.0_55"
569       }, {
570         "class": "sun.reflect.NativeMethodAccessorImpl",
571         "method": "invoke",
572         "line": -1,
573         "exact": false,
574         "location": "?",
575         "version": "1.7.0_55"
576       }, {
577         "class": "sun.reflect.DelegatingMethodAccessorImpl",
578         "method": "invoke",
579         "line": -1,
580         "exact": false,
581         "location": "?",
582         "version": "1.7.0_55"
583       }, {
584         "class": "java.lang.reflect.Method",
585         "method": "invoke",
586         "line": -1,
587         "exact": false,
588         "location": "?",
589         "version": "1.7.0_55"
590       }, {
591         "class": "org.junit.runners.model.FrameworkMethod$1",
592         "method": "runReflectiveCall",
593         "file": "FrameworkMethod.java",
594         "line": 47,
595         "exact": true,
596         "location": "junit-4.11.jar",
597         "version": "?"
598       }, {
599         "class": "org.junit.internal.runners.model.ReflectiveCallable",
600         "method": "run",
601         "file": "ReflectiveCallable.java",
602         "line": 12,
603         "exact": true,
604         "location": "junit-4.11.jar",
605         "version": "?"
606       }, {
607         "class": "org.junit.runners.model.FrameworkMethod",
608         "method": "invokeExplosively",
609         "file": "FrameworkMethod.java",
610         "line": 44,
611         "exact": true,
612         "location": "junit-4.11.jar",
613         "version": "?"
614       }, {
615         "class": "org.junit.internal.runners.statements.InvokeMethod",
616         "method": "evaluate",
617         "file": "InvokeMethod.java",
618         "line": 17,
619         "exact": true,
620         "location": "junit-4.11.jar",
621         "version": "?"
622       }, {
623         "class": "org.junit.runners.ParentRunner",
624         "method": "runLeaf",
625         "file": "ParentRunner.java",
626         "line": 271,
627         "exact": true,
628         "location": "junit-4.11.jar",
629         "version": "?"
630       }, {
631         "class": "org.junit.runners.BlockJUnit4ClassRunner",
632         "method": "runChild",
633         "file": "BlockJUnit4ClassRunner.java",
634         "line": 70,
635         "exact": true,
636         "location": "junit-4.11.jar",
637         "version": "?"
638       }, {
639         "class": "org.junit.runners.BlockJUnit4ClassRunner",
640         "method": "runChild",
641         "file": "BlockJUnit4ClassRunner.java",
642         "line": 50,
643         "exact": true,
644         "location": "junit-4.11.jar",
645         "version": "?"
646       }, {
647         "class": "org.junit.runners.ParentRunner$3",
648         "method": "run",
649         "file": "ParentRunner.java",
650         "line": 238,
651         "exact": true,
652         "location": "junit-4.11.jar",
653         "version": "?"
654       }, {
655         "class": "org.junit.runners.ParentRunner$1",
656         "method": "schedule",
657         "file": "ParentRunner.java",
658         "line": 63,
659         "exact": true,
660         "location": "junit-4.11.jar",
661         "version": "?"
662       }, {
663         "class": "org.junit.runners.ParentRunner",
664         "method": "runChildren",
665         "file": "ParentRunner.java",
666         "line": 236,
667         "exact": true,
668         "location": "junit-4.11.jar",
669         "version": "?"
670       }, {
671         "class": "org.junit.runners.ParentRunner",
672         "method": "access$000",
673         "file": "ParentRunner.java",
674         "line": 53,
675         "exact": true,
676         "location": "junit-4.11.jar",
677         "version": "?"
678       }, {
679         "class": "org.junit.runners.ParentRunner$2",
680         "method": "evaluate",
681         "file": "ParentRunner.java",
682         "line": 229,
683         "exact": true,
684         "location": "junit-4.11.jar",
685         "version": "?"
686       }, {
687         "class": "org.junit.internal.runners.statements.RunBefores",
688         "method": "evaluate",
689         "file": "RunBefores.java",
690         "line": 26,
691         "exact": true,
692         "location": "junit-4.11.jar",
693         "version": "?"
694       }, {
695         "class": "org.junit.internal.runners.statements.RunAfters",
696         "method": "evaluate",
697         "file": "RunAfters.java",
698         "line": 27,
699         "exact": true,
700         "location": "junit-4.11.jar",
701         "version": "?"
702       }, {
703         "class": "org.junit.runners.ParentRunner",
704         "method": "run",
705         "file": "ParentRunner.java",
706         "line": 309,
707         "exact": true,
708         "location": "junit-4.11.jar",
709         "version": "?"
710       }, {
711         "class": "org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference",
712         "method": "run",
713         "file": "JUnit4TestReference.java",
714         "line": 50,
715         "exact": true,
716         "location": ".cp/",
717         "version": "?"
718       }, {
719         "class": "org.eclipse.jdt.internal.junit.runner.TestExecution",
720         "method": "run",
721         "file": "TestExecution.java",
722         "line": 38,
723         "exact": true,
724         "location": ".cp/",
725         "version": "?"
726       }, {
727         "class": "org.eclipse.jdt.internal.junit.runner.RemoteTestRunner",
728         "method": "runTests",
729         "file": "RemoteTestRunner.java",
730         "line": 467,
731         "exact": true,
732         "location": ".cp/",
733         "version": "?"
734       }, {
735         "class": "org.eclipse.jdt.internal.junit.runner.RemoteTestRunner",
736         "method": "runTests",
737         "file": "RemoteTestRunner.java",
738         "line": 683,
739         "exact": true,
740         "location": ".cp/",
741         "version": "?"
742       }, {
743         "class": "org.eclipse.jdt.internal.junit.runner.RemoteTestRunner",
744         "method": "run",
745         "file": "RemoteTestRunner.java",
746         "line": 390,
747         "exact": true,
748         "location": ".cp/",
749         "version": "?"
750       }, {
751         "class": "org.eclipse.jdt.internal.junit.runner.RemoteTestRunner",
752         "method": "main",
753         "file": "RemoteTestRunner.java",
754         "line": 197,
755         "exact": true,
756         "location": ".cp/",
757         "version": "?"
758       }],
759       "localizedMessage": "I am suppressed exception 2",
760       "message": "I am suppressed exception 2",
761       "name": "java.lang.IndexOutOfBoundsException"
762     }]
763   },
764   "loggerFQCN": "f.q.c.n",
765   "endOfBatch": false,
766   "contextMap": [{
767     "key": "MDC.B",
768     "value": "B_Value"
769   }, {
770     "key": "MDC.A",
771     "value": "A_Value"
772   }],
773   "contextStack": ["stack_msg1", "stack_msg2"],
774   "source": {
775     "class": "org.apache.logging.log4j.core.layout.LogEventFixtures",
776     "method": "createLogEvent",
777     "file": "LogEventFixtures.java",
778     "line": 54
779   }
780 }
781  * </pre>
782  * <p>
783  * If {@code complete="false"}, the appender does not write the JSON open array character "[" at the start
784  * of the document, "]" and the end, nor comma "," between records.
785  * </p>
786  * <p>
787  * This approach enforces the independence of the JsonLayout and the appender where you embed it.
788  * </p>
789  * <h3>Encoding</h3>
790  * <p>
791  * Appenders using this layout should have their {@code charset} set to {@code UTF-8} or {@code UTF-16}, otherwise
792  * events containing non ASCII characters could result in corrupted log files.
793  * </p>
794  * <h3>Pretty vs. compact XML</h3>
795  * <p>
796  * By default, the JSON layout is not compact (a.k.a. "pretty") with {@code compact="false"}, which means the
797  * appender uses end-of-line characters and indents lines to format the text. If {@code compact="true"}, then no
798  * end-of-line or indentation is used. Message content may contain, of course, escaped end-of-lines.
799  * </p>
800  */
801 @Plugin(name = "JsonLayout", category = Node.CATEGORY, elementType = Layout.ELEMENT_TYPE, printObject = true)
802 public final class JsonLayout extends AbstractJacksonLayout {
803 
804     private static final String DEFAULT_FOOTER = "]";
805 
806     private static final String DEFAULT_HEADER = "[";
807 
808     static final String CONTENT_TYPE = "application/json";
809 
810     protected JsonLayout(final Configuration config, final boolean locationInfo, final boolean properties,
811             final boolean encodeThreadContextAsList,
812             final boolean complete, final boolean compact, final boolean eventEol, final String headerPattern,
813             final String footerPattern, final Charset charset) {
814         super(config, new JacksonFactory.JSON(encodeThreadContextAsList).newWriter(locationInfo, properties, compact),
815                 charset, compact, complete, eventEol,
816                 PatternLayout.createSerializer(config, null, headerPattern, DEFAULT_HEADER, null, false, false),
817                 PatternLayout.createSerializer(config, null, footerPattern, DEFAULT_FOOTER, null, false, false));
818     }
819 
820     /**
821      * Returns appropriate JSON header.
822      *
823      * @return a byte array containing the header, opening the JSON array.
824      */
825     @Override
826     public byte[] getHeader() {
827         if (!this.complete) {
828             return null;
829         }
830         final StringBuilder buf = new StringBuilder();
831         final String str = serializeToString(getHeaderSerializer());
832         if (str != null) {
833             buf.append(str);
834         }
835         buf.append(this.eol);
836         return getBytes(buf.toString());
837     }
838 
839     /**
840      * Returns appropriate JSON footer.
841      *
842      * @return a byte array containing the footer, closing the JSON array.
843      */
844     @Override
845     public byte[] getFooter() {
846         if (!this.complete) {
847             return null;
848         }
849         final StringBuilder buf = new StringBuilder();
850         buf.append(this.eol);
851         final String str = serializeToString(getFooterSerializer());
852         if (str != null) {
853             buf.append(str);
854         }
855         buf.append(this.eol);
856         return getBytes(buf.toString());
857     }
858 
859     @Override
860     public Map<String, String> getContentFormat() {
861         final Map<String, String> result = new HashMap<>();
862         result.put("version", "2.0");
863         return result;
864     }
865 
866     @Override
867     /**
868      * @return The content type.
869      */
870     public String getContentType() {
871         return CONTENT_TYPE + "; charset=" + this.getCharset();
872     }
873 
874     /**
875      * Creates a JSON Layout.
876      * @param config
877      *           The plugin configuration.
878      * @param locationInfo
879      *            If "true", includes the location information in the generated JSON.
880      * @param properties
881      *            If "true", includes the thread context map in the generated JSON.
882      * @param propertiesAsList
883      *            If true, the thread context map is included as a list of map entry objects, where each entry has
884      *            a "key" attribute (whose value is the key) and a "value" attribute (whose value is the value).
885      *            Defaults to false, in which case the thread context map is included as a simple map of key-value
886      *            pairs.
887      * @param complete
888      *            If "true", includes the JSON header and footer, and comma between records.
889      * @param compact
890      *            If "true", does not use end-of-lines and indentation, defaults to "false".
891      * @param eventEol
892      *            If "true", forces an EOL after each log event (even if compact is "true"), defaults to "false". This
893      *            allows one even per line, even in compact mode.
894      * @param headerPattern
895      *            The header pattern, defaults to {@code "["} if null.
896      * @param footerPattern
897      *            The header pattern, defaults to {@code "]"} if null.
898      * @param charset
899      *            The character set to use, if {@code null}, uses "UTF-8".
900      * @return A JSON Layout.
901      */
902     @PluginFactory
903     public static JsonLayout createLayout(
904             // @formatter:off
905             @PluginConfiguration final Configuration config,
906             @PluginAttribute(value = "locationInfo", defaultBoolean = false) final boolean locationInfo,
907             @PluginAttribute(value = "properties", defaultBoolean = false) final boolean properties,
908             @PluginAttribute(value = "propertiesAsList", defaultBoolean = false) final boolean propertiesAsList,
909             @PluginAttribute(value = "complete", defaultBoolean = false) final boolean complete,
910             @PluginAttribute(value = "compact", defaultBoolean = false) final boolean compact,
911             @PluginAttribute(value = "eventEol", defaultBoolean = false) final boolean eventEol,
912             @PluginAttribute(value = "header", defaultString = DEFAULT_HEADER) final String headerPattern,
913             @PluginAttribute(value = "footer", defaultString = DEFAULT_FOOTER) final String footerPattern,
914             @PluginAttribute(value = "charset", defaultString = "UTF-8") final Charset charset
915             // @formatter:on
916     ) {
917         final boolean encodeThreadContextAsList = properties && propertiesAsList;
918         return new JsonLayout(config, locationInfo, properties, encodeThreadContextAsList, complete, compact, eventEol,
919                 headerPattern, footerPattern, charset);
920     }
921 
922     /**
923      * Creates a JSON Layout using the default settings. Useful for testing.
924      *
925      * @return A JSON Layout.
926      */
927     public static JsonLayout createDefaultLayout() {
928         return new JsonLayout(new DefaultConfiguration(), false, false, false, false, false, false,
929                 DEFAULT_HEADER, DEFAULT_FOOTER, StandardCharsets.UTF_8);
930     }
931 
932     @Override
933     public void toSerializable(final LogEvent event, final Writer writer) throws IOException {
934         if (complete && eventCount > 0) {
935             writer.append(", ");
936         }
937         super.toSerializable(event, writer);
938     }
939 }