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