001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements. See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache license, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License. You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the license for the specific language governing permissions and
015     * limitations under the license.
016     */
017    package org.apache.logging.log4j.core.layout;
018    
019    import java.nio.charset.Charset;
020    import java.util.HashMap;
021    import java.util.Map;
022    
023    import org.apache.logging.log4j.core.config.plugins.Plugin;
024    import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
025    import org.apache.logging.log4j.core.config.plugins.PluginFactory;
026    import org.apache.logging.log4j.core.util.Charsets;
027    
028    /**
029     * Appends a series of JSON events as strings serialized as bytes.
030     *
031     * <h4>Complete well-formed JSON vs. fragment JSON</h4>
032     * <p>
033     * If you configure {@code complete="true"}, the appender outputs a well-formed JSON document. By default, with {@code complete="false"},
034     * you should include the output as an <em>external file</em> in a separate file to form a well-formed JSON document.
035     * </p>
036     * <p>
037     * A well-formed JSON event follows this pattern:
038     * </p>
039     *
040     * <pre>
041     * {
042      "timeMillis": 1,
043      "thread": "MyThreadName",
044      "level": "DEBUG",
045      "loggerName": "a.B",
046      "marker": {
047        "name": "Marker1",
048        "parents": [{
049          "name": "ParentMarker1",
050          "parents": [{
051            "name": "GrandMotherMarker"
052          }, {
053            "name": "GrandFatherMarker"
054          }]
055        }, {
056          "name": "GrandFatherMarker"
057        }]
058      },
059      "message": "Msg",
060      "thrown": {
061        "cause": {
062          "commonElementCount": 27,
063          "extendedStackTrace": [{
064            "class": "org.apache.logging.log4j.core.layout.LogEventFixtures",
065            "method": "createLogEvent",
066            "file": "LogEventFixtures.java",
067            "line": 53,
068            "exact": false,
069            "location": "test-classes/",
070            "version": "?"
071          }],
072          "localizedMessage": "testNPEx",
073          "message": "testNPEx",
074          "name": "java.lang.NullPointerException"
075        },
076        "commonElementCount": 0,
077        "extendedStackTrace": [{
078          "class": "org.apache.logging.log4j.core.layout.LogEventFixtures",
079          "method": "createLogEvent",
080          "file": "LogEventFixtures.java",
081          "line": 56,
082          "exact": true,
083          "location": "test-classes/",
084          "version": "?"
085        }, {
086          "class": "org.apache.logging.log4j.core.layout.JsonLayoutTest",
087          "method": "testAllFeatures",
088          "file": "JsonLayoutTest.java",
089          "line": 105,
090          "exact": true,
091          "location": "test-classes/",
092          "version": "?"
093        }, {
094          "class": "org.apache.logging.log4j.core.layout.JsonLayoutTest",
095          "method": "testLocationOnCompactOnMdcOn",
096          "file": "JsonLayoutTest.java",
097          "line": 268,
098          "exact": true,
099          "location": "test-classes/",
100          "version": "?"
101        }, {
102          "class": "sun.reflect.NativeMethodAccessorImpl",
103          "method": "invoke",
104          "line": -1,
105          "exact": false,
106          "location": "?",
107          "version": "1.7.0_55"
108        }, {
109          "class": "sun.reflect.NativeMethodAccessorImpl",
110          "method": "invoke",
111          "line": -1,
112          "exact": false,
113          "location": "?",
114          "version": "1.7.0_55"
115        }, {
116          "class": "sun.reflect.DelegatingMethodAccessorImpl",
117          "method": "invoke",
118          "line": -1,
119          "exact": false,
120          "location": "?",
121          "version": "1.7.0_55"
122        }, {
123          "class": "java.lang.reflect.Method",
124          "method": "invoke",
125          "line": -1,
126          "exact": false,
127          "location": "?",
128          "version": "1.7.0_55"
129        }, {
130          "class": "org.junit.runners.model.FrameworkMethod$1",
131          "method": "runReflectiveCall",
132          "file": "FrameworkMethod.java",
133          "line": 47,
134          "exact": true,
135          "location": "junit-4.11.jar",
136          "version": "?"
137        }, {
138          "class": "org.junit.internal.runners.model.ReflectiveCallable",
139          "method": "run",
140          "file": "ReflectiveCallable.java",
141          "line": 12,
142          "exact": true,
143          "location": "junit-4.11.jar",
144          "version": "?"
145        }, {
146          "class": "org.junit.runners.model.FrameworkMethod",
147          "method": "invokeExplosively",
148          "file": "FrameworkMethod.java",
149          "line": 44,
150          "exact": true,
151          "location": "junit-4.11.jar",
152          "version": "?"
153        }, {
154          "class": "org.junit.internal.runners.statements.InvokeMethod",
155          "method": "evaluate",
156          "file": "InvokeMethod.java",
157          "line": 17,
158          "exact": true,
159          "location": "junit-4.11.jar",
160          "version": "?"
161        }, {
162          "class": "org.junit.runners.ParentRunner",
163          "method": "runLeaf",
164          "file": "ParentRunner.java",
165          "line": 271,
166          "exact": true,
167          "location": "junit-4.11.jar",
168          "version": "?"
169        }, {
170          "class": "org.junit.runners.BlockJUnit4ClassRunner",
171          "method": "runChild",
172          "file": "BlockJUnit4ClassRunner.java",
173          "line": 70,
174          "exact": true,
175          "location": "junit-4.11.jar",
176          "version": "?"
177        }, {
178          "class": "org.junit.runners.BlockJUnit4ClassRunner",
179          "method": "runChild",
180          "file": "BlockJUnit4ClassRunner.java",
181          "line": 50,
182          "exact": true,
183          "location": "junit-4.11.jar",
184          "version": "?"
185        }, {
186          "class": "org.junit.runners.ParentRunner$3",
187          "method": "run",
188          "file": "ParentRunner.java",
189          "line": 238,
190          "exact": true,
191          "location": "junit-4.11.jar",
192          "version": "?"
193        }, {
194          "class": "org.junit.runners.ParentRunner$1",
195          "method": "schedule",
196          "file": "ParentRunner.java",
197          "line": 63,
198          "exact": true,
199          "location": "junit-4.11.jar",
200          "version": "?"
201        }, {
202          "class": "org.junit.runners.ParentRunner",
203          "method": "runChildren",
204          "file": "ParentRunner.java",
205          "line": 236,
206          "exact": true,
207          "location": "junit-4.11.jar",
208          "version": "?"
209        }, {
210          "class": "org.junit.runners.ParentRunner",
211          "method": "access$000",
212          "file": "ParentRunner.java",
213          "line": 53,
214          "exact": true,
215          "location": "junit-4.11.jar",
216          "version": "?"
217        }, {
218          "class": "org.junit.runners.ParentRunner$2",
219          "method": "evaluate",
220          "file": "ParentRunner.java",
221          "line": 229,
222          "exact": true,
223          "location": "junit-4.11.jar",
224          "version": "?"
225        }, {
226          "class": "org.junit.internal.runners.statements.RunBefores",
227          "method": "evaluate",
228          "file": "RunBefores.java",
229          "line": 26,
230          "exact": true,
231          "location": "junit-4.11.jar",
232          "version": "?"
233        }, {
234          "class": "org.junit.internal.runners.statements.RunAfters",
235          "method": "evaluate",
236          "file": "RunAfters.java",
237          "line": 27,
238          "exact": true,
239          "location": "junit-4.11.jar",
240          "version": "?"
241        }, {
242          "class": "org.junit.runners.ParentRunner",
243          "method": "run",
244          "file": "ParentRunner.java",
245          "line": 309,
246          "exact": true,
247          "location": "junit-4.11.jar",
248          "version": "?"
249        }, {
250          "class": "org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference",
251          "method": "run",
252          "file": "JUnit4TestReference.java",
253          "line": 50,
254          "exact": true,
255          "location": ".cp/",
256          "version": "?"
257        }, {
258          "class": "org.eclipse.jdt.internal.junit.runner.TestExecution",
259          "method": "run",
260          "file": "TestExecution.java",
261          "line": 38,
262          "exact": true,
263          "location": ".cp/",
264          "version": "?"
265        }, {
266          "class": "org.eclipse.jdt.internal.junit.runner.RemoteTestRunner",
267          "method": "runTests",
268          "file": "RemoteTestRunner.java",
269          "line": 467,
270          "exact": true,
271          "location": ".cp/",
272          "version": "?"
273        }, {
274          "class": "org.eclipse.jdt.internal.junit.runner.RemoteTestRunner",
275          "method": "runTests",
276          "file": "RemoteTestRunner.java",
277          "line": 683,
278          "exact": true,
279          "location": ".cp/",
280          "version": "?"
281        }, {
282          "class": "org.eclipse.jdt.internal.junit.runner.RemoteTestRunner",
283          "method": "run",
284          "file": "RemoteTestRunner.java",
285          "line": 390,
286          "exact": true,
287          "location": ".cp/",
288          "version": "?"
289        }, {
290          "class": "org.eclipse.jdt.internal.junit.runner.RemoteTestRunner",
291          "method": "main",
292          "file": "RemoteTestRunner.java",
293          "line": 197,
294          "exact": true,
295          "location": ".cp/",
296          "version": "?"
297        }],
298        "localizedMessage": "testIOEx",
299        "message": "testIOEx",
300        "name": "java.io.IOException",
301        "suppressed": [{
302          "commonElementCount": 0,
303          "extendedStackTrace": [{
304            "class": "org.apache.logging.log4j.core.layout.LogEventFixtures",
305            "method": "createLogEvent",
306            "file": "LogEventFixtures.java",
307            "line": 57,
308            "exact": true,
309            "location": "test-classes/",
310            "version": "?"
311          }, {
312            "class": "org.apache.logging.log4j.core.layout.JsonLayoutTest",
313            "method": "testAllFeatures",
314            "file": "JsonLayoutTest.java",
315            "line": 105,
316            "exact": true,
317            "location": "test-classes/",
318            "version": "?"
319          }, {
320            "class": "org.apache.logging.log4j.core.layout.JsonLayoutTest",
321            "method": "testLocationOnCompactOnMdcOn",
322            "file": "JsonLayoutTest.java",
323            "line": 268,
324            "exact": true,
325            "location": "test-classes/",
326            "version": "?"
327          }, {
328            "class": "sun.reflect.NativeMethodAccessorImpl",
329            "method": "invoke",
330            "line": -1,
331            "exact": false,
332            "location": "?",
333            "version": "1.7.0_55"
334          }, {
335            "class": "sun.reflect.NativeMethodAccessorImpl",
336            "method": "invoke",
337            "line": -1,
338            "exact": false,
339            "location": "?",
340            "version": "1.7.0_55"
341          }, {
342            "class": "sun.reflect.DelegatingMethodAccessorImpl",
343            "method": "invoke",
344            "line": -1,
345            "exact": false,
346            "location": "?",
347            "version": "1.7.0_55"
348          }, {
349            "class": "java.lang.reflect.Method",
350            "method": "invoke",
351            "line": -1,
352            "exact": false,
353            "location": "?",
354            "version": "1.7.0_55"
355          }, {
356            "class": "org.junit.runners.model.FrameworkMethod$1",
357            "method": "runReflectiveCall",
358            "file": "FrameworkMethod.java",
359            "line": 47,
360            "exact": true,
361            "location": "junit-4.11.jar",
362            "version": "?"
363          }, {
364            "class": "org.junit.internal.runners.model.ReflectiveCallable",
365            "method": "run",
366            "file": "ReflectiveCallable.java",
367            "line": 12,
368            "exact": true,
369            "location": "junit-4.11.jar",
370            "version": "?"
371          }, {
372            "class": "org.junit.runners.model.FrameworkMethod",
373            "method": "invokeExplosively",
374            "file": "FrameworkMethod.java",
375            "line": 44,
376            "exact": true,
377            "location": "junit-4.11.jar",
378            "version": "?"
379          }, {
380            "class": "org.junit.internal.runners.statements.InvokeMethod",
381            "method": "evaluate",
382            "file": "InvokeMethod.java",
383            "line": 17,
384            "exact": true,
385            "location": "junit-4.11.jar",
386            "version": "?"
387          }, {
388            "class": "org.junit.runners.ParentRunner",
389            "method": "runLeaf",
390            "file": "ParentRunner.java",
391            "line": 271,
392            "exact": true,
393            "location": "junit-4.11.jar",
394            "version": "?"
395          }, {
396            "class": "org.junit.runners.BlockJUnit4ClassRunner",
397            "method": "runChild",
398            "file": "BlockJUnit4ClassRunner.java",
399            "line": 70,
400            "exact": true,
401            "location": "junit-4.11.jar",
402            "version": "?"
403          }, {
404            "class": "org.junit.runners.BlockJUnit4ClassRunner",
405            "method": "runChild",
406            "file": "BlockJUnit4ClassRunner.java",
407            "line": 50,
408            "exact": true,
409            "location": "junit-4.11.jar",
410            "version": "?"
411          }, {
412            "class": "org.junit.runners.ParentRunner$3",
413            "method": "run",
414            "file": "ParentRunner.java",
415            "line": 238,
416            "exact": true,
417            "location": "junit-4.11.jar",
418            "version": "?"
419          }, {
420            "class": "org.junit.runners.ParentRunner$1",
421            "method": "schedule",
422            "file": "ParentRunner.java",
423            "line": 63,
424            "exact": true,
425            "location": "junit-4.11.jar",
426            "version": "?"
427          }, {
428            "class": "org.junit.runners.ParentRunner",
429            "method": "runChildren",
430            "file": "ParentRunner.java",
431            "line": 236,
432            "exact": true,
433            "location": "junit-4.11.jar",
434            "version": "?"
435          }, {
436            "class": "org.junit.runners.ParentRunner",
437            "method": "access$000",
438            "file": "ParentRunner.java",
439            "line": 53,
440            "exact": true,
441            "location": "junit-4.11.jar",
442            "version": "?"
443          }, {
444            "class": "org.junit.runners.ParentRunner$2",
445            "method": "evaluate",
446            "file": "ParentRunner.java",
447            "line": 229,
448            "exact": true,
449            "location": "junit-4.11.jar",
450            "version": "?"
451          }, {
452            "class": "org.junit.internal.runners.statements.RunBefores",
453            "method": "evaluate",
454            "file": "RunBefores.java",
455            "line": 26,
456            "exact": true,
457            "location": "junit-4.11.jar",
458            "version": "?"
459          }, {
460            "class": "org.junit.internal.runners.statements.RunAfters",
461            "method": "evaluate",
462            "file": "RunAfters.java",
463            "line": 27,
464            "exact": true,
465            "location": "junit-4.11.jar",
466            "version": "?"
467          }, {
468            "class": "org.junit.runners.ParentRunner",
469            "method": "run",
470            "file": "ParentRunner.java",
471            "line": 309,
472            "exact": true,
473            "location": "junit-4.11.jar",
474            "version": "?"
475          }, {
476            "class": "org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference",
477            "method": "run",
478            "file": "JUnit4TestReference.java",
479            "line": 50,
480            "exact": true,
481            "location": ".cp/",
482            "version": "?"
483          }, {
484            "class": "org.eclipse.jdt.internal.junit.runner.TestExecution",
485            "method": "run",
486            "file": "TestExecution.java",
487            "line": 38,
488            "exact": true,
489            "location": ".cp/",
490            "version": "?"
491          }, {
492            "class": "org.eclipse.jdt.internal.junit.runner.RemoteTestRunner",
493            "method": "runTests",
494            "file": "RemoteTestRunner.java",
495            "line": 467,
496            "exact": true,
497            "location": ".cp/",
498            "version": "?"
499          }, {
500            "class": "org.eclipse.jdt.internal.junit.runner.RemoteTestRunner",
501            "method": "runTests",
502            "file": "RemoteTestRunner.java",
503            "line": 683,
504            "exact": true,
505            "location": ".cp/",
506            "version": "?"
507          }, {
508            "class": "org.eclipse.jdt.internal.junit.runner.RemoteTestRunner",
509            "method": "run",
510            "file": "RemoteTestRunner.java",
511            "line": 390,
512            "exact": true,
513            "location": ".cp/",
514            "version": "?"
515          }, {
516            "class": "org.eclipse.jdt.internal.junit.runner.RemoteTestRunner",
517            "method": "main",
518            "file": "RemoteTestRunner.java",
519            "line": 197,
520            "exact": true,
521            "location": ".cp/",
522            "version": "?"
523          }],
524          "localizedMessage": "I am suppressed exception 1",
525          "message": "I am suppressed exception 1",
526          "name": "java.lang.IndexOutOfBoundsException"
527        }, {
528          "commonElementCount": 0,
529          "extendedStackTrace": [{
530            "class": "org.apache.logging.log4j.core.layout.LogEventFixtures",
531            "method": "createLogEvent",
532            "file": "LogEventFixtures.java",
533            "line": 58,
534            "exact": true,
535            "location": "test-classes/",
536            "version": "?"
537          }, {
538            "class": "org.apache.logging.log4j.core.layout.JsonLayoutTest",
539            "method": "testAllFeatures",
540            "file": "JsonLayoutTest.java",
541            "line": 105,
542            "exact": true,
543            "location": "test-classes/",
544            "version": "?"
545          }, {
546            "class": "org.apache.logging.log4j.core.layout.JsonLayoutTest",
547            "method": "testLocationOnCompactOnMdcOn",
548            "file": "JsonLayoutTest.java",
549            "line": 268,
550            "exact": true,
551            "location": "test-classes/",
552            "version": "?"
553          }, {
554            "class": "sun.reflect.NativeMethodAccessorImpl",
555            "method": "invoke",
556            "line": -1,
557            "exact": false,
558            "location": "?",
559            "version": "1.7.0_55"
560          }, {
561            "class": "sun.reflect.NativeMethodAccessorImpl",
562            "method": "invoke",
563            "line": -1,
564            "exact": false,
565            "location": "?",
566            "version": "1.7.0_55"
567          }, {
568            "class": "sun.reflect.DelegatingMethodAccessorImpl",
569            "method": "invoke",
570            "line": -1,
571            "exact": false,
572            "location": "?",
573            "version": "1.7.0_55"
574          }, {
575            "class": "java.lang.reflect.Method",
576            "method": "invoke",
577            "line": -1,
578            "exact": false,
579            "location": "?",
580            "version": "1.7.0_55"
581          }, {
582            "class": "org.junit.runners.model.FrameworkMethod$1",
583            "method": "runReflectiveCall",
584            "file": "FrameworkMethod.java",
585            "line": 47,
586            "exact": true,
587            "location": "junit-4.11.jar",
588            "version": "?"
589          }, {
590            "class": "org.junit.internal.runners.model.ReflectiveCallable",
591            "method": "run",
592            "file": "ReflectiveCallable.java",
593            "line": 12,
594            "exact": true,
595            "location": "junit-4.11.jar",
596            "version": "?"
597          }, {
598            "class": "org.junit.runners.model.FrameworkMethod",
599            "method": "invokeExplosively",
600            "file": "FrameworkMethod.java",
601            "line": 44,
602            "exact": true,
603            "location": "junit-4.11.jar",
604            "version": "?"
605          }, {
606            "class": "org.junit.internal.runners.statements.InvokeMethod",
607            "method": "evaluate",
608            "file": "InvokeMethod.java",
609            "line": 17,
610            "exact": true,
611            "location": "junit-4.11.jar",
612            "version": "?"
613          }, {
614            "class": "org.junit.runners.ParentRunner",
615            "method": "runLeaf",
616            "file": "ParentRunner.java",
617            "line": 271,
618            "exact": true,
619            "location": "junit-4.11.jar",
620            "version": "?"
621          }, {
622            "class": "org.junit.runners.BlockJUnit4ClassRunner",
623            "method": "runChild",
624            "file": "BlockJUnit4ClassRunner.java",
625            "line": 70,
626            "exact": true,
627            "location": "junit-4.11.jar",
628            "version": "?"
629          }, {
630            "class": "org.junit.runners.BlockJUnit4ClassRunner",
631            "method": "runChild",
632            "file": "BlockJUnit4ClassRunner.java",
633            "line": 50,
634            "exact": true,
635            "location": "junit-4.11.jar",
636            "version": "?"
637          }, {
638            "class": "org.junit.runners.ParentRunner$3",
639            "method": "run",
640            "file": "ParentRunner.java",
641            "line": 238,
642            "exact": true,
643            "location": "junit-4.11.jar",
644            "version": "?"
645          }, {
646            "class": "org.junit.runners.ParentRunner$1",
647            "method": "schedule",
648            "file": "ParentRunner.java",
649            "line": 63,
650            "exact": true,
651            "location": "junit-4.11.jar",
652            "version": "?"
653          }, {
654            "class": "org.junit.runners.ParentRunner",
655            "method": "runChildren",
656            "file": "ParentRunner.java",
657            "line": 236,
658            "exact": true,
659            "location": "junit-4.11.jar",
660            "version": "?"
661          }, {
662            "class": "org.junit.runners.ParentRunner",
663            "method": "access$000",
664            "file": "ParentRunner.java",
665            "line": 53,
666            "exact": true,
667            "location": "junit-4.11.jar",
668            "version": "?"
669          }, {
670            "class": "org.junit.runners.ParentRunner$2",
671            "method": "evaluate",
672            "file": "ParentRunner.java",
673            "line": 229,
674            "exact": true,
675            "location": "junit-4.11.jar",
676            "version": "?"
677          }, {
678            "class": "org.junit.internal.runners.statements.RunBefores",
679            "method": "evaluate",
680            "file": "RunBefores.java",
681            "line": 26,
682            "exact": true,
683            "location": "junit-4.11.jar",
684            "version": "?"
685          }, {
686            "class": "org.junit.internal.runners.statements.RunAfters",
687            "method": "evaluate",
688            "file": "RunAfters.java",
689            "line": 27,
690            "exact": true,
691            "location": "junit-4.11.jar",
692            "version": "?"
693          }, {
694            "class": "org.junit.runners.ParentRunner",
695            "method": "run",
696            "file": "ParentRunner.java",
697            "line": 309,
698            "exact": true,
699            "location": "junit-4.11.jar",
700            "version": "?"
701          }, {
702            "class": "org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference",
703            "method": "run",
704            "file": "JUnit4TestReference.java",
705            "line": 50,
706            "exact": true,
707            "location": ".cp/",
708            "version": "?"
709          }, {
710            "class": "org.eclipse.jdt.internal.junit.runner.TestExecution",
711            "method": "run",
712            "file": "TestExecution.java",
713            "line": 38,
714            "exact": true,
715            "location": ".cp/",
716            "version": "?"
717          }, {
718            "class": "org.eclipse.jdt.internal.junit.runner.RemoteTestRunner",
719            "method": "runTests",
720            "file": "RemoteTestRunner.java",
721            "line": 467,
722            "exact": true,
723            "location": ".cp/",
724            "version": "?"
725          }, {
726            "class": "org.eclipse.jdt.internal.junit.runner.RemoteTestRunner",
727            "method": "runTests",
728            "file": "RemoteTestRunner.java",
729            "line": 683,
730            "exact": true,
731            "location": ".cp/",
732            "version": "?"
733          }, {
734            "class": "org.eclipse.jdt.internal.junit.runner.RemoteTestRunner",
735            "method": "run",
736            "file": "RemoteTestRunner.java",
737            "line": 390,
738            "exact": true,
739            "location": ".cp/",
740            "version": "?"
741          }, {
742            "class": "org.eclipse.jdt.internal.junit.runner.RemoteTestRunner",
743            "method": "main",
744            "file": "RemoteTestRunner.java",
745            "line": 197,
746            "exact": true,
747            "location": ".cp/",
748            "version": "?"
749          }],
750          "localizedMessage": "I am suppressed exception 2",
751          "message": "I am suppressed exception 2",
752          "name": "java.lang.IndexOutOfBoundsException"
753        }]
754      },
755      "loggerFQCN": "f.q.c.n",
756      "endOfBatch": false,
757      "contextMap": [{
758        "key": "MDC.B",
759        "value": "B_Value"
760      }, {
761        "key": "MDC.A",
762        "value": "A_Value"
763      }],
764      "contextStack": ["stack_msg1", "stack_msg2"],
765      "source": {
766        "class": "org.apache.logging.log4j.core.layout.LogEventFixtures",
767        "method": "createLogEvent",
768        "file": "LogEventFixtures.java",
769        "line": 54
770      }
771    }
772     * </pre>
773     * <p>
774     * If {@code complete="false"}, the appender does not write the JSON open array character "[" at the start of the document. and "]" and the
775     * end.
776     * </p>
777     * <p>
778     * This approach enforces the independence of the JsonLayout and the appender where you embed it.
779     * </p>
780     * <h4>Encoding</h4>
781     * <p>
782     * Appenders using this layout should have their {@code charset} set to {@code UTF-8} or {@code UTF-16}, otherwise events containing non
783     * ASCII characters could result in corrupted log files.
784     * </p>
785     * <h4>Pretty vs. compact XML</h4>
786     * <p>
787     * By default, the JSON layout is not compact (a.k.a. not "pretty") with {@code compact="false"}, which means the appender uses end-of-line
788     * characters and indents lines to format the text. If {@code compact="true"}, then no end-of-line or indentation is used. Message content
789     * may contain, of course, escaped end-of-lines.
790     * </p>
791     */
792    @Plugin(name = "JsonLayout", category = "Core", elementType = "layout", printObject = true)
793    public final class JsonLayout extends AbstractJacksonLayout {
794    
795        protected JsonLayout(final boolean locationInfo, final boolean properties, final boolean complete, final boolean compact,
796                final Charset charset) {
797            super(new JacksonFactory.JSON().newWriter(locationInfo, properties, compact), charset, compact, complete);
798        }
799    
800        /**
801         * Returns appropriate JSON headers.
802         *
803         * @return a byte array containing the header, opening the JSON array.
804         */
805        @Override
806        public byte[] getHeader() {
807            if (!this.complete) {
808                return null;
809            }
810            final StringBuilder buf = new StringBuilder();
811            buf.append('[');
812            buf.append(this.eol);
813            return buf.toString().getBytes(this.getCharset());
814        }
815    
816        /**
817         * Returns appropriate JSON footer.
818         *
819         * @return a byte array containing the footer, closing the JSON array.
820         */
821        @Override
822        public byte[] getFooter() {
823            if (!this.complete) {
824                return null;
825            }
826            return (this.eol + ']' + this.eol).getBytes(this.getCharset());
827        }
828    
829        @Override
830        public Map<String, String> getContentFormat() {
831            final Map<String, String> result = new HashMap<String, String>();
832            result.put("version", "2.0");
833            return result;
834        }
835    
836        @Override
837        /**
838         * @return The content type.
839         */
840        public String getContentType() {
841            return "application/json; charset=" + this.getCharset();
842        }
843    
844        /**
845         * Creates a JSON Layout.
846         *
847         * @param locationInfo If "true", includes the location information in the generated JSON.
848         * @param properties If "true", includes the thread context in the generated JSON.
849         * @param complete If "true", includes the JSON header and footer, defaults to "false".
850         * @param compact If "true", does not use end-of-lines and indentation, defaults to "false".
851         * @param charset The character set to use, if {@code null}, uses "UTF-8".
852         * @return A JSON Layout.
853         */
854        @PluginFactory
855        public static AbstractJacksonLayout createLayout(
856                // @formatter:off
857                @PluginAttribute(value = "locationInfo", defaultBoolean = false) final boolean locationInfo,
858                @PluginAttribute(value = "properties", defaultBoolean = false) final boolean properties,
859                @PluginAttribute(value = "complete", defaultBoolean = false) final boolean complete,
860                @PluginAttribute(value = "compact", defaultBoolean = false) final boolean compact,
861                @PluginAttribute(value = "charset", defaultString = "UTF-8") final Charset charset
862                // @formatter:on
863        ) {
864            return new JsonLayout(locationInfo, properties, complete, compact, charset);
865        }
866    
867        /**
868         * Creates a JSON Layout using the default settings.
869         *
870         * @return A JSON Layout.
871         */
872        public static AbstractJacksonLayout createDefaultLayout() {
873            return new JsonLayout(false, false, false, false, Charsets.UTF_8);
874        }
875    }