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 org.apache.logging.log4j.core.LogEvent;
020    import org.apache.logging.log4j.core.config.Configuration;
021    import org.apache.logging.log4j.core.config.plugins.Plugin;
022    import org.apache.logging.log4j.core.config.plugins.PluginAttr;
023    import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
024    import org.apache.logging.log4j.core.config.plugins.PluginElement;
025    import org.apache.logging.log4j.core.config.plugins.PluginFactory;
026    import org.apache.logging.log4j.core.helpers.Charsets;
027    import org.apache.logging.log4j.core.helpers.OptionConverter;
028    import org.apache.logging.log4j.core.pattern.LogEventPatternConverter;
029    import org.apache.logging.log4j.core.pattern.PatternFormatter;
030    import org.apache.logging.log4j.core.pattern.PatternParser;
031    import org.apache.logging.log4j.core.pattern.RegexReplacement;
032    
033    import java.nio.charset.Charset;
034    import java.util.HashMap;
035    import java.util.List;
036    import java.util.Map;
037    
038    /**
039     * <p>A flexible layout configurable with pattern string. The goal of this class
040     * is to {@link org.apache.logging.log4j.core.Layout#toByteArray format} a {@link LogEvent} and return the results.
041     * The format of the result depends on the <em>conversion pattern</em>.
042     * <p>
043     * <p/>
044     * <p>The conversion pattern is closely related to the conversion
045     * pattern of the printf function in C. A conversion pattern is
046     * composed of literal text and format control expressions called
047     * <em>conversion specifiers</em>.
048     *
049     * See the Log4j Manual for details on the supported pattern converters.
050     */
051    @Plugin(name = "PatternLayout", category = "Core", elementType = "layout", printObject = true)
052    public final class PatternLayout extends AbstractStringLayout {
053        /**
054         * Default pattern string for log output. Currently set to the
055         * string <b>"%m%n"</b> which just prints the application supplied
056         * message.
057         */
058        public static final String DEFAULT_CONVERSION_PATTERN = "%m%n";
059    
060        /**
061         * A conversion pattern equivalent to the TTCCCLayout.
062         * Current value is <b>%r [%t] %p %c %x - %m%n</b>.
063         */
064        public static final String TTCC_CONVERSION_PATTERN =
065            "%r [%t] %p %c %x - %m%n";
066    
067        /**
068         * A simple pattern.
069         * Current value is <b>%d [%t] %p %c - %m%n</b>.
070         */
071        public static final String SIMPLE_CONVERSION_PATTERN =
072            "%d [%t] %p %c - %m%n";
073    
074        /** Key to identify pattern converters. */
075        public static final String KEY = "Converter";
076    
077        /**
078         * Initial converter for pattern.
079         */
080        private List<PatternFormatter> formatters;
081    
082        /**
083         * Conversion pattern.
084         */
085        private final String conversionPattern;
086    
087    
088        /**
089         * The current Configuration.
090         */
091        private final Configuration config;
092    
093        private final RegexReplacement replace;
094    
095        private final boolean handleExceptions;
096    
097        /**
098         * Constructs a EnhancedPatternLayout using the supplied conversion pattern.
099         *
100         * @param config The Configuration.
101         * @param replace The regular expression to match.
102         * @param pattern conversion pattern.
103         * @param charset The character set.
104         * @param handleExceptions Whether or not exceptions should always be handled in this pattern (if {@code true},
105         *                         exceptions will be written even if the pattern does not specify so).
106         */
107        private PatternLayout(final Configuration config, final RegexReplacement replace, final String pattern,
108                              final Charset charset, boolean handleExceptions) {
109            super(charset);
110            this.replace = replace;
111            this.conversionPattern = pattern;
112            this.config = config;
113            this.handleExceptions = handleExceptions;
114            final PatternParser parser = createPatternParser(config);
115            formatters = parser.parse(pattern == null ? DEFAULT_CONVERSION_PATTERN : pattern, this.handleExceptions);
116        }
117    
118        /**
119         * Set the <b>ConversionPattern</b> option. This is the string which
120         * controls formatting and consists of a mix of literal content and
121         * conversion specifiers.
122         *
123         * @param conversionPattern conversion pattern.
124         */
125        public void setConversionPattern(final String conversionPattern) {
126            final String pattern = OptionConverter.convertSpecialChars(conversionPattern);
127            if (pattern == null) {
128                return;
129            }
130            final PatternParser parser = createPatternParser(this.config);
131            formatters = parser.parse(pattern, this.handleExceptions);
132        }
133    
134        public String getConversionPattern() {
135            return conversionPattern;
136        }
137    
138        /**
139         * PatternLayout's content format is specified by:<p/>
140         * Key: "structured" Value: "false"<p/>
141         * Key: "formatType" Value: "conversion" (format uses the keywords supported by OptionConverter)<p/>
142         * Key: "format" Value: provided "conversionPattern" param
143         * @return Map of content format keys supporting PatternLayout
144         */
145        @Override
146        public Map<String, String> getContentFormat()
147        {
148            Map<String, String> result = new HashMap<String, String>();
149            result.put("structured", "false");
150            result.put("formatType", "conversion");
151            result.put("format", conversionPattern);
152            return result;
153        }
154    
155        /**
156         * Formats a logging event to a writer.
157         *
158         *
159         * @param event logging event to be formatted.
160         * @return The event formatted as a String.
161         */
162        @Override
163        public String toSerializable(final LogEvent event) {
164            final StringBuilder buf = new StringBuilder();
165            for (final PatternFormatter formatter : formatters) {
166                formatter.format(event, buf);
167            }
168            String str = buf.toString();
169            if (replace != null) {
170                str = replace.format(str);
171            }
172            return str;
173        }
174    
175        /**
176         * Create a PatternParser.
177         * @param config The Configuration.
178         * @return The PatternParser.
179         */
180        public static PatternParser createPatternParser(final Configuration config) {
181            if (config == null) {
182                return new PatternParser(config, KEY, LogEventPatternConverter.class);
183            }
184            PatternParser parser = (PatternParser) config.getComponent(KEY);
185            if (parser == null) {
186                parser = new PatternParser(config, KEY, LogEventPatternConverter.class);
187                config.addComponent(KEY, parser);
188                parser = (PatternParser) config.getComponent(KEY);
189            }
190            return parser;
191        }
192    
193        @Override
194        public String toString() {
195            return conversionPattern;
196        }
197    
198        /**
199         * Create a pattern layout.
200         *
201         * @param pattern The pattern. If not specified, defaults to DEFAULT_CONVERSION_PATTERN.
202         * @param config The Configuration. Some Converters require access to the Interpolator.
203         * @param replace A Regex replacement String.
204         * @param charsetName The character set.
205         * @param suppressExceptions Whether or not exceptions should be suppressed in this pattern (defaults to no, which
206         *                           means exceptions will be written even if the pattern does not specify so).
207         * @return The PatternLayout.
208         */
209        @PluginFactory
210        public static PatternLayout createLayout(@PluginAttr("pattern") final String pattern,
211                                                 @PluginConfiguration final Configuration config,
212                                                 @PluginElement("replace") final RegexReplacement replace,
213                                                 @PluginAttr("charset") final String charsetName,
214                                                 @PluginAttr("suppressExceptions") final String suppressExceptions) {
215            final Charset charset = Charsets.getSupportedCharset(charsetName);
216            boolean handleExceptions = suppressExceptions == null || !Boolean.parseBoolean(suppressExceptions);
217            return new PatternLayout(config, replace, pattern == null ? DEFAULT_CONVERSION_PATTERN : pattern, charset,
218                    handleExceptions);
219        }
220    }