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.Arrays;
021import java.util.HashMap;
022import java.util.List;
023import java.util.Map;
024
025import org.apache.logging.log4j.core.Layout;
026import org.apache.logging.log4j.core.LogEvent;
027import org.apache.logging.log4j.core.config.Configuration;
028import org.apache.logging.log4j.core.config.DefaultConfiguration;
029import org.apache.logging.log4j.core.config.Node;
030import org.apache.logging.log4j.core.config.plugins.Plugin;
031import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
032import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
033import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
034import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
035import org.apache.logging.log4j.core.config.plugins.PluginElement;
036import org.apache.logging.log4j.core.config.plugins.PluginFactory;
037import org.apache.logging.log4j.core.impl.LocationAware;
038import org.apache.logging.log4j.core.pattern.FormattingInfo;
039import org.apache.logging.log4j.core.pattern.LogEventPatternConverter;
040import org.apache.logging.log4j.core.pattern.PatternFormatter;
041import org.apache.logging.log4j.core.pattern.PatternParser;
042import org.apache.logging.log4j.core.pattern.RegexReplacement;
043import org.apache.logging.log4j.util.PropertiesUtil;
044import org.apache.logging.log4j.util.Strings;
045
046/**
047 * A flexible layout configurable with pattern string.
048 * <p>
049 * The goal of this class is to {@link org.apache.logging.log4j.core.Layout#toByteArray format} a {@link LogEvent} and
050 * return the results. The format of the result depends on the <em>conversion pattern</em>.
051 * </p>
052 * <p>
053 * The conversion pattern is closely related to the conversion pattern of the printf function in C. A conversion pattern
054 * is composed of literal text and format control expressions called <em>conversion specifiers</em>.
055 * </p>
056 * <p>
057 * See the Log4j Manual for details on the supported pattern converters.
058 * </p>
059 */
060@Plugin(name = "PatternLayout", category = Node.CATEGORY, elementType = Layout.ELEMENT_TYPE, printObject = true)
061public final class PatternLayout extends AbstractStringLayout {
062
063    /**
064     * Default pattern string for log output. Currently set to the string <b>"%m%n"</b> which just prints the
065     * application supplied message.
066     */
067    public static final String DEFAULT_CONVERSION_PATTERN = "%m%n";
068
069    /**
070     * A conversion pattern equivalent to the TTCCLayout. Current value is <b>%r [%t] %p %c %notEmpty{%x }- %m%n</b>.
071     */
072    public static final String TTCC_CONVERSION_PATTERN = "%r [%t] %p %c %notEmpty{%x }- %m%n";
073
074    /**
075     * A simple pattern. Current value is <b>%d [%t] %p %c - %m%n</b>.
076     */
077    public static final String SIMPLE_CONVERSION_PATTERN = "%d [%t] %p %c - %m%n";
078
079    /** Key to identify pattern converters. */
080    public static final String KEY = "Converter";
081
082    /**
083     * Conversion pattern.
084     */
085    private final String conversionPattern;
086    private final PatternSelector patternSelector;
087    private final Serializer eventSerializer;
088
089    /**
090     * Constructs a PatternLayout using the supplied conversion pattern.
091     *
092     * @param config The Configuration.
093     * @param replace The regular expression to match.
094     * @param eventPattern conversion pattern.
095     * @param patternSelector The PatternSelector.
096     * @param charset The character set.
097     * @param alwaysWriteExceptions Whether or not exceptions should always be handled in this pattern (if {@code true},
098     *                         exceptions will be written even if the pattern does not specify so).
099     * @param disableAnsi
100     *            If {@code "true"}, do not output ANSI escape codes
101     * @param noConsoleNoAnsi
102     *            If {@code "true"} (default) and {@link System#console()} is null, do not output ANSI escape codes
103     * @param headerPattern header conversion pattern.
104     * @param footerPattern footer conversion pattern.
105     */
106    private PatternLayout(final Configuration config, final RegexReplacement replace, final String eventPattern,
107            final PatternSelector patternSelector, final Charset charset, final boolean alwaysWriteExceptions,
108            final boolean disableAnsi, final boolean noConsoleNoAnsi, final String headerPattern,
109            final String footerPattern) {
110        super(config, charset,
111                newSerializerBuilder()
112                        .setConfiguration(config)
113                        .setReplace(replace)
114                        .setPatternSelector(patternSelector)
115                        .setAlwaysWriteExceptions(alwaysWriteExceptions)
116                        .setDisableAnsi(disableAnsi)
117                        .setNoConsoleNoAnsi(noConsoleNoAnsi)
118                        .setPattern(headerPattern)
119                        .build(),
120                newSerializerBuilder()
121                        .setConfiguration(config)
122                        .setReplace(replace)
123                        .setPatternSelector(patternSelector)
124                        .setAlwaysWriteExceptions(alwaysWriteExceptions)
125                        .setDisableAnsi(disableAnsi)
126                        .setNoConsoleNoAnsi(noConsoleNoAnsi)
127                        .setPattern(footerPattern)
128                        .build());
129        this.conversionPattern = eventPattern;
130        this.patternSelector = patternSelector;
131        this.eventSerializer = newSerializerBuilder()
132                .setConfiguration(config)
133                .setReplace(replace)
134                .setPatternSelector(patternSelector)
135                .setAlwaysWriteExceptions(alwaysWriteExceptions)
136                .setDisableAnsi(disableAnsi)
137                .setNoConsoleNoAnsi(noConsoleNoAnsi)
138                .setPattern(eventPattern)
139                .setDefaultPattern(DEFAULT_CONVERSION_PATTERN)
140                .build();
141    }
142
143    public static SerializerBuilder newSerializerBuilder() {
144        return new SerializerBuilder();
145    }
146
147    @Override
148    public boolean requiresLocation() {
149        return eventSerializer instanceof LocationAware && ((LocationAware) eventSerializer).requiresLocation();
150    }
151
152
153    /**
154     * Deprecated, use {@link #newSerializerBuilder()} instead.
155     *
156     * @param configuration
157     * @param replace
158     * @param pattern
159     * @param defaultPattern
160     * @param patternSelector
161     * @param alwaysWriteExceptions
162     * @param noConsoleNoAnsi
163     * @return a new Serializer.
164     * @deprecated Use {@link #newSerializerBuilder()} instead.
165     */
166    @Deprecated
167    public static Serializer createSerializer(final Configuration configuration, final RegexReplacement replace,
168            final String pattern, final String defaultPattern, final PatternSelector patternSelector,
169            final boolean alwaysWriteExceptions, final boolean noConsoleNoAnsi) {
170        final SerializerBuilder builder = newSerializerBuilder();
171        builder.setAlwaysWriteExceptions(alwaysWriteExceptions);
172        builder.setConfiguration(configuration);
173        builder.setDefaultPattern(defaultPattern);
174        builder.setNoConsoleNoAnsi(noConsoleNoAnsi);
175        builder.setPattern(pattern);
176        builder.setPatternSelector(patternSelector);
177        builder.setReplace(replace);
178        return builder.build();
179    }
180
181    /**
182     * Gets the conversion pattern.
183     *
184     * @return the conversion pattern.
185     */
186    public String getConversionPattern() {
187        return conversionPattern;
188    }
189
190    /**
191     * Gets this PatternLayout's content format. Specified by:
192     * <ul>
193     * <li>Key: "structured" Value: "false"</li>
194     * <li>Key: "formatType" Value: "conversion" (format uses the keywords supported by OptionConverter)</li>
195     * <li>Key: "format" Value: provided "conversionPattern" param</li>
196     * </ul>
197     *
198     * @return Map of content format keys supporting PatternLayout
199     */
200    @Override
201    public Map<String, String> getContentFormat() {
202        final Map<String, String> result = new HashMap<>();
203        result.put("structured", "false");
204        result.put("formatType", "conversion");
205        result.put("format", conversionPattern);
206        return result;
207    }
208
209    /**
210     * Formats a logging event to a writer.
211     *
212     * @param event logging event to be formatted.
213     * @return The event formatted as a String.
214     */
215    @Override
216    public String toSerializable(final LogEvent event) {
217        return eventSerializer.toSerializable(event);
218    }
219
220    public void serialize(final LogEvent event, StringBuilder stringBuilder) {
221        eventSerializer.toSerializable(event, stringBuilder);
222    }
223
224    @Override
225    public void encode(final LogEvent event, final ByteBufferDestination destination) {
226        final StringBuilder text = toText(eventSerializer, event, getStringBuilder());
227        final Encoder<StringBuilder> encoder = getStringBuilderEncoder();
228        encoder.encode(text, destination);
229        trimToMaxSize(text);
230    }
231
232    /**
233     * Creates a text representation of the specified log event
234     * and writes it into the specified StringBuilder.
235     * <p>
236     * Implementations are free to return a new StringBuilder if they can
237     * detect in advance that the specified StringBuilder is too small.
238     */
239    private StringBuilder toText(final Serializer2 serializer, final LogEvent event,
240            final StringBuilder destination) {
241        return serializer.toSerializable(event, destination);
242    }
243
244    /**
245     * Creates a PatternParser.
246     * @param config The Configuration.
247     * @return The PatternParser.
248     */
249    public static PatternParser createPatternParser(final Configuration config) {
250        if (config == null) {
251            return new PatternParser(config, KEY, LogEventPatternConverter.class);
252        }
253        PatternParser parser = config.getComponent(KEY);
254        if (parser == null) {
255            parser = new PatternParser(config, KEY, LogEventPatternConverter.class);
256            config.addComponent(KEY, parser);
257            parser = config.getComponent(KEY);
258        }
259        return parser;
260    }
261
262    @Override
263    public String toString() {
264        return patternSelector == null ? conversionPattern : patternSelector.toString();
265    }
266
267    /**
268     * Creates a pattern layout.
269     *
270     * @param pattern
271     *        The pattern. If not specified, defaults to DEFAULT_CONVERSION_PATTERN.
272     * @param patternSelector
273     *        Allows different patterns to be used based on some selection criteria.
274     * @param config
275     *        The Configuration. Some Converters require access to the Interpolator.
276     * @param replace
277     *        A Regex replacement String.
278     * @param charset
279     *        The character set. The platform default is used if not specified.
280     * @param alwaysWriteExceptions
281     *        If {@code "true"} (default) exceptions are always written even if the pattern contains no exception tokens.
282     * @param noConsoleNoAnsi
283     *        If {@code "true"} (default is false) and {@link System#console()} is null, do not output ANSI escape codes
284     * @param headerPattern
285     *        The footer to place at the top of the document, once.
286     * @param footerPattern
287     *        The footer to place at the bottom of the document, once.
288     * @return The PatternLayout.
289     * @deprecated Use {@link #newBuilder()} instead. This will be private in a future version.
290     */
291    @PluginFactory
292    @Deprecated
293    public static PatternLayout createLayout(
294            @PluginAttribute(value = "pattern", defaultString = DEFAULT_CONVERSION_PATTERN) final String pattern,
295            @PluginElement("PatternSelector") final PatternSelector patternSelector,
296            @PluginConfiguration final Configuration config,
297            @PluginElement("Replace") final RegexReplacement replace,
298            // LOG4J2-783 use platform default by default, so do not specify defaultString for charset
299            @PluginAttribute(value = "charset") final Charset charset,
300            @PluginAttribute(value = "alwaysWriteExceptions", defaultBoolean = true) final boolean alwaysWriteExceptions,
301            @PluginAttribute(value = "noConsoleNoAnsi") final boolean noConsoleNoAnsi,
302            @PluginAttribute("header") final String headerPattern,
303            @PluginAttribute("footer") final String footerPattern) {
304        return newBuilder()
305            .withPattern(pattern)
306            .withPatternSelector(patternSelector)
307            .withConfiguration(config)
308            .withRegexReplacement(replace)
309            .withCharset(charset)
310            .withAlwaysWriteExceptions(alwaysWriteExceptions)
311            .withNoConsoleNoAnsi(noConsoleNoAnsi)
312            .withHeader(headerPattern)
313            .withFooter(footerPattern)
314            .build();
315    }
316
317    private interface PatternSerializer extends Serializer, Serializer2, LocationAware {}
318
319    private static final class NoFormatPatternSerializer implements PatternSerializer {
320
321        private final LogEventPatternConverter[] converters;
322
323        private NoFormatPatternSerializer(final PatternFormatter[] formatters) {
324            this.converters = new LogEventPatternConverter[formatters.length];
325            for (int i = 0; i < formatters.length; i++) {
326                converters[i] = formatters[i].getConverter();
327            }
328        }
329
330        @Override
331        public String toSerializable(final LogEvent event) {
332            final StringBuilder sb = getStringBuilder();
333            try {
334                return toSerializable(event, sb).toString();
335            } finally {
336                trimToMaxSize(sb);
337            }
338        }
339
340        @Override
341        public StringBuilder toSerializable(final LogEvent event, final StringBuilder buffer) {
342            for (LogEventPatternConverter converter : converters) {
343                converter.format(event, buffer);
344            }
345            return buffer;
346        }
347
348        @Override
349        public boolean requiresLocation() {
350            for (LogEventPatternConverter converter : converters) {
351                if (converter instanceof LocationAware && ((LocationAware) converter).requiresLocation()) {
352                    return true;
353                }
354            }
355            return false;
356        }
357
358        @Override
359        public String toString() {
360            return super.toString() + "[converters=" + Arrays.toString(converters) + "]";
361        }
362    }
363
364    private static final class PatternFormatterPatternSerializer implements PatternSerializer {
365
366        private final PatternFormatter[] formatters;
367
368        private PatternFormatterPatternSerializer(final PatternFormatter[] formatters) {
369            this.formatters = formatters;
370        }
371
372        @Override
373        public String toSerializable(final LogEvent event) {
374            final StringBuilder sb = getStringBuilder();
375            try {
376                return toSerializable(event, sb).toString();
377            } finally {
378                trimToMaxSize(sb);
379            }
380        }
381
382        @Override
383        public StringBuilder toSerializable(final LogEvent event, final StringBuilder buffer) {
384            for (PatternFormatter formatter : formatters) {
385                formatter.format(event, buffer);
386            }
387            return buffer;
388        }
389
390        @Override
391        public boolean requiresLocation() {
392            for (PatternFormatter formatter : formatters) {
393                if (formatter.requiresLocation()) {
394                    return true;
395                }
396            }
397            return false;
398        }
399
400        @Override
401        public String toString() {
402            return super.toString() +
403                    "[formatters=" +
404                    Arrays.toString(formatters) +
405                    "]";
406        }
407    }
408
409    private static final class PatternSerializerWithReplacement implements Serializer, Serializer2, LocationAware {
410
411        private final PatternSerializer delegate;
412        private final RegexReplacement replace;
413
414        private PatternSerializerWithReplacement(final PatternSerializer delegate, final RegexReplacement replace) {
415            this.delegate = delegate;
416            this.replace = replace;
417        }
418
419        @Override
420        public String toSerializable(final LogEvent event) {
421            final StringBuilder sb = getStringBuilder();
422            try {
423                return toSerializable(event, sb).toString();
424            } finally {
425                trimToMaxSize(sb);
426            }
427        }
428
429        @Override
430        public StringBuilder toSerializable(final LogEvent event, final StringBuilder buf) {
431            StringBuilder buffer = delegate.toSerializable(event, buf);
432            String str = buffer.toString();
433            str = replace.format(str);
434            buffer.setLength(0);
435            buffer.append(str);
436            return buffer;
437        }
438
439        @Override
440        public boolean requiresLocation() {
441            return delegate.requiresLocation();
442        }
443
444        @Override
445        public String toString() {
446            return super.toString() +
447                    "[delegate=" +
448                    delegate +
449                    ", replace=" +
450                    replace +
451                    "]";
452        }
453    }
454
455    public static class SerializerBuilder implements org.apache.logging.log4j.core.util.Builder<Serializer> {
456
457        private Configuration configuration;
458        private RegexReplacement replace;
459        private String pattern;
460        private String defaultPattern;
461        private PatternSelector patternSelector;
462        private boolean alwaysWriteExceptions;
463        private boolean disableAnsi;
464        private boolean noConsoleNoAnsi;
465
466        @Override
467        public Serializer build() {
468            if (Strings.isEmpty(pattern) && Strings.isEmpty(defaultPattern)) {
469                return null;
470            }
471            if (patternSelector == null) {
472                try {
473                    final PatternParser parser = createPatternParser(configuration);
474                    final List<PatternFormatter> list = parser.parse(pattern == null ? defaultPattern : pattern,
475                            alwaysWriteExceptions, disableAnsi, noConsoleNoAnsi);
476                    final PatternFormatter[] formatters = list.toArray(PatternFormatter.EMPTY_ARRAY);
477                    boolean hasFormattingInfo = false;
478                    for (PatternFormatter formatter : formatters) {
479                        FormattingInfo info = formatter.getFormattingInfo();
480                        if (info != null && info != FormattingInfo.getDefault()) {
481                            hasFormattingInfo = true;
482                            break;
483                        }
484                    }
485                    PatternSerializer serializer = hasFormattingInfo
486                            ? new PatternFormatterPatternSerializer(formatters)
487                            : new NoFormatPatternSerializer(formatters);
488                    return replace == null ? serializer : new PatternSerializerWithReplacement(serializer, replace);
489                } catch (final RuntimeException ex) {
490                    throw new IllegalArgumentException("Cannot parse pattern '" + pattern + "'", ex);
491                }
492            }
493            return new PatternSelectorSerializer(patternSelector, replace);
494        }
495
496        public SerializerBuilder setConfiguration(final Configuration configuration) {
497            this.configuration = configuration;
498            return this;
499        }
500
501        public SerializerBuilder setReplace(final RegexReplacement replace) {
502            this.replace = replace;
503            return this;
504        }
505
506        public SerializerBuilder setPattern(final String pattern) {
507            this.pattern = pattern;
508            return this;
509        }
510
511        public SerializerBuilder setDefaultPattern(final String defaultPattern) {
512            this.defaultPattern = defaultPattern;
513            return this;
514        }
515
516        public SerializerBuilder setPatternSelector(final PatternSelector patternSelector) {
517            this.patternSelector = patternSelector;
518            return this;
519        }
520
521        public SerializerBuilder setAlwaysWriteExceptions(final boolean alwaysWriteExceptions) {
522            this.alwaysWriteExceptions = alwaysWriteExceptions;
523            return this;
524        }
525
526        public SerializerBuilder setDisableAnsi(final boolean disableAnsi) {
527            this.disableAnsi = disableAnsi;
528            return this;
529        }
530
531        public SerializerBuilder setNoConsoleNoAnsi(final boolean noConsoleNoAnsi) {
532            this.noConsoleNoAnsi = noConsoleNoAnsi;
533            return this;
534        }
535
536    }
537
538    private static final class PatternSelectorSerializer implements Serializer, Serializer2, LocationAware {
539
540        private final PatternSelector patternSelector;
541        private final RegexReplacement replace;
542
543        private PatternSelectorSerializer(final PatternSelector patternSelector, final RegexReplacement replace) {
544            this.patternSelector = patternSelector;
545            this.replace = replace;
546        }
547
548        @Override
549        public String toSerializable(final LogEvent event) {
550            final StringBuilder sb = getStringBuilder();
551            try {
552                return toSerializable(event, sb).toString();
553            } finally {
554                trimToMaxSize(sb);
555            }
556        }
557
558        @Override
559        public StringBuilder toSerializable(final LogEvent event, final StringBuilder buffer) {
560            for (PatternFormatter formatter : patternSelector.getFormatters(event)) {
561                formatter.format(event, buffer);
562            }
563            if (replace != null) { // creates temporary objects
564                String str = buffer.toString();
565                str = replace.format(str);
566                buffer.setLength(0);
567                buffer.append(str);
568            }
569            return buffer;
570        }
571
572        @Override
573        public boolean requiresLocation() {
574            return patternSelector instanceof LocationAware && ((LocationAware) patternSelector).requiresLocation();
575        }
576
577        @Override
578        public String toString() {
579            final StringBuilder builder = new StringBuilder();
580            builder.append(super.toString());
581            builder.append("[patternSelector=");
582            builder.append(patternSelector);
583            builder.append(", replace=");
584            builder.append(replace);
585            builder.append("]");
586            return builder.toString();
587        }
588    }
589
590    /**
591     * Creates a PatternLayout using the default options. These options include using UTF-8, the default conversion
592     * pattern, exceptions being written, and with ANSI escape codes.
593     *
594     * @return the PatternLayout.
595     * @see #DEFAULT_CONVERSION_PATTERN Default conversion pattern
596     */
597    public static PatternLayout createDefaultLayout() {
598        return newBuilder().build();
599    }
600
601    /**
602     * Creates a PatternLayout using the default options and the given configuration. These options include using UTF-8,
603     * the default conversion pattern, exceptions being written, and with ANSI escape codes.
604     *
605     * @param configuration The Configuration.
606     *
607     * @return the PatternLayout.
608     * @see #DEFAULT_CONVERSION_PATTERN Default conversion pattern
609     */
610    public static PatternLayout createDefaultLayout(final Configuration configuration) {
611        return newBuilder().withConfiguration(configuration).build();
612    }
613
614    /**
615     * Creates a builder for a custom PatternLayout.
616     *
617     * @return a PatternLayout builder.
618     */
619    @PluginBuilderFactory
620    public static Builder newBuilder() {
621        return new Builder();
622    }
623
624    /**
625     * Custom PatternLayout builder. Use the {@link PatternLayout#newBuilder() builder factory method} to create this.
626     */
627    public static class Builder implements org.apache.logging.log4j.core.util.Builder<PatternLayout> {
628
629        @PluginBuilderAttribute
630        private String pattern = PatternLayout.DEFAULT_CONVERSION_PATTERN;
631
632        @PluginElement("PatternSelector")
633        private PatternSelector patternSelector;
634
635        @PluginConfiguration
636        private Configuration configuration;
637
638        @PluginElement("Replace")
639        private RegexReplacement regexReplacement;
640
641        // LOG4J2-783 use platform default by default
642        @PluginBuilderAttribute
643        private Charset charset = Charset.defaultCharset();
644
645        @PluginBuilderAttribute
646        private boolean alwaysWriteExceptions = true;
647
648        @PluginBuilderAttribute
649        private boolean disableAnsi = !useAnsiEscapeCodes();
650
651        @PluginBuilderAttribute
652        private boolean noConsoleNoAnsi;
653
654        @PluginBuilderAttribute
655        private String header;
656
657        @PluginBuilderAttribute
658        private String footer;
659
660        private Builder() {
661        }
662
663        private boolean useAnsiEscapeCodes() {
664            final PropertiesUtil propertiesUtil = PropertiesUtil.getProperties();
665            final boolean isPlatformSupportsAnsi = !propertiesUtil.isOsWindows();
666            final boolean isJansiRequested = !propertiesUtil.getBooleanProperty("log4j.skipJansi", true);
667            return isPlatformSupportsAnsi || isJansiRequested;
668        }
669
670        /**
671         * @param pattern
672         *        The pattern. If not specified, defaults to DEFAULT_CONVERSION_PATTERN.
673         */
674        public Builder withPattern(final String pattern) {
675            this.pattern = pattern;
676            return this;
677        }
678
679        /**
680         * @param patternSelector
681         *        Allows different patterns to be used based on some selection criteria.
682         */
683        public Builder withPatternSelector(final PatternSelector patternSelector) {
684            this.patternSelector = patternSelector;
685            return this;
686        }
687
688        /**
689         * @param configuration
690         *        The Configuration. Some Converters require access to the Interpolator.
691         */
692        public Builder withConfiguration(final Configuration configuration) {
693            this.configuration = configuration;
694            return this;
695        }
696
697        /**
698         * @param regexReplacement
699         *        A Regex replacement
700         */
701        public Builder withRegexReplacement(final RegexReplacement regexReplacement) {
702            this.regexReplacement = regexReplacement;
703            return this;
704        }
705
706        /**
707         * @param charset
708         *        The character set. The platform default is used if not specified.
709         */
710        public Builder withCharset(final Charset charset) {
711            // LOG4J2-783 if null, use platform default by default
712            if (charset != null) {
713                this.charset = charset;
714            }
715            return this;
716        }
717
718        /**
719         * @param alwaysWriteExceptions
720         *        If {@code "true"} (default) exceptions are always written even if the pattern contains no exception tokens.
721         */
722        public Builder withAlwaysWriteExceptions(final boolean alwaysWriteExceptions) {
723            this.alwaysWriteExceptions = alwaysWriteExceptions;
724            return this;
725        }
726
727        /**
728         * @param disableAnsi
729         *        If {@code "true"} (default is value of system property `log4j.skipJansi`, or `true` if undefined),
730         *        do not output ANSI escape codes
731         */
732        public Builder withDisableAnsi(final boolean disableAnsi) {
733            this.disableAnsi = disableAnsi;
734            return this;
735        }
736
737        /**
738         * @param noConsoleNoAnsi
739         *        If {@code "true"} (default is false) and {@link System#console()} is null, do not output ANSI escape codes
740         */
741        public Builder withNoConsoleNoAnsi(final boolean noConsoleNoAnsi) {
742            this.noConsoleNoAnsi = noConsoleNoAnsi;
743            return this;
744        }
745
746        /**
747         * @param header
748         *        The footer to place at the top of the document, once.
749         */
750        public Builder withHeader(final String header) {
751            this.header = header;
752            return this;
753        }
754
755        /**
756         * @param footer
757         *        The footer to place at the bottom of the document, once.
758         */
759        public Builder withFooter(final String footer) {
760            this.footer = footer;
761            return this;
762        }
763
764        @Override
765        public PatternLayout build() {
766            // fall back to DefaultConfiguration
767            if (configuration == null) {
768                configuration = new DefaultConfiguration();
769            }
770            return new PatternLayout(configuration, regexReplacement, pattern, patternSelector, charset,
771                alwaysWriteExceptions, disableAnsi, noConsoleNoAnsi, header, footer);
772        }
773    }
774
775    public Serializer getEventSerializer() {
776        return eventSerializer;
777    }
778}