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.pattern;
018    
019    import org.apache.logging.log4j.core.LogEvent;
020    import org.apache.logging.log4j.core.config.plugins.Plugin;
021    
022    import java.io.PrintWriter;
023    import java.io.StringWriter;
024    
025    
026    /**
027     * Outputs the Throwable portion of the LoggingEvent as a full stacktrace
028     * unless this converter's option is 'short', where it just outputs the first line of the trace, or if
029     * the number of lines to print is explicitly specified.
030     */
031    @Plugin(name = "ThrowablePatternConverter", type = "Converter")
032    @ConverterKeys({"ex", "throwable", "exception" })
033    public class ThrowablePatternConverter extends LogEventPatternConverter {
034    
035        /**
036         * Format the whole stack trace.
037         */
038        protected static final String FULL = "full";
039        /**
040         * Format only the first line of the throwable.
041         */
042        protected static final String SHORT = "short";
043        /**
044         * If "short", only first line of throwable report will be formatted.<br>
045         * If "full", the whole stack trace will be formatted.<br>
046         * If "numeric" the output will be limited to the specified number of lines.
047         */
048        protected final String option;
049    
050        /**
051         * The number of lines to write.
052         */
053        protected final int lines;
054    
055        /**
056         * Constructor.
057         * @param name Name of converter.
058         * @param style CSS style for output.
059         * @param options options, may be null.
060         */
061        protected ThrowablePatternConverter(String name, String style, final String[] options) {
062            super(name, style);
063            int count = 0;
064            if ((options != null) && (options.length > 0)) {
065                option = options[0];
066                if (option == null) {
067                } else if (option.equalsIgnoreCase(SHORT)) {
068                    count = 2;
069                } else if (!option.equalsIgnoreCase(FULL)) {
070                    count = Integer.parseInt(option);
071                }
072    
073            } else {
074                option = null;
075            }
076            lines = count;
077        }
078    
079        /**
080         * Gets an instance of the class.
081         *
082         * @param options pattern options, may be null.  If first element is "short",
083         *                only the first line of the throwable will be formatted.
084         * @return instance of class.
085         */
086        public static ThrowablePatternConverter newInstance(final String[] options) {
087            return new ThrowablePatternConverter("Throwable", "throwable", options);
088        }
089    
090        /**
091         * {@inheritDoc}
092         */
093        @Override
094        public void format(final LogEvent event, final StringBuilder toAppendTo) {
095            Throwable t = event.getThrown();
096    
097            if (t != null) {
098                StringWriter w = new StringWriter();
099                t.printStackTrace(new PrintWriter(w));
100                int len = toAppendTo.length();
101                if (len > 0 && !Character.isWhitespace(toAppendTo.charAt(len - 1))) {
102                    toAppendTo.append(" ");
103                }
104                if (lines > 0) {
105                    StringBuilder sb = new StringBuilder();
106                    String[] array = w.toString().split("\n");
107                    for (int i = 0; i < lines; ++i) {
108                        sb.append(array[i]).append("\n");
109                    }
110                    toAppendTo.append(sb.toString());
111    
112                } else {
113                    toAppendTo.append(w.toString());
114                }
115            }
116        }
117    
118        /**
119         * This converter obviously handles throwables.
120         *
121         * @return true.
122         */
123        @Override
124        public boolean handlesThrowable() {
125            return true;
126        }
127    }