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