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