/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache license, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the license for the specific language governing permissions and
 * limitations under the license.
 */
package org.apache.logging.log4j.core.pattern;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.impl.ThrowableFormatOptions;
import org.apache.logging.log4j.core.layout.PatternLayout;
import org.apache.logging.log4j.core.util.StringBuilderWriter;
import org.apache.logging.log4j.util.Strings;


Outputs the Throwable portion of the LoggingEvent as a full stack trace unless this converter's option is 'short', where it just outputs the first line of the trace, or if the number of lines to print is explicitly specified.
/** * Outputs the Throwable portion of the LoggingEvent as a full stack trace * unless this converter's option is 'short', where it just outputs the first line of the trace, or if * the number of lines to print is explicitly specified. */
@Plugin(name = "ThrowablePatternConverter", category = PatternConverter.CATEGORY) @ConverterKeys({ "ex", "throwable", "exception" }) public class ThrowablePatternConverter extends LogEventPatternConverter {
Lists PatternFormatters for the suffix attribute.
/** * Lists {@link PatternFormatter}s for the suffix attribute. */
protected final List<PatternFormatter> formatters; private String rawOption; private final boolean subShortOption; private final boolean nonStandardLineSeparator;
Options.
/** * Options. */
protected final ThrowableFormatOptions options;
Constructor.
Params:
  • name – Name of converter.
  • style – CSS style for output.
  • options – options, may be null.
Deprecated:Use ThrowablePatternConverter(String name, String stule, String[] options, Configuration config)
/** * Constructor. * @param name Name of converter. * @param style CSS style for output. * @param options options, may be null. * @deprecated Use ThrowablePatternConverter(String name, String stule, String[] options, Configuration config) */
@Deprecated protected ThrowablePatternConverter(final String name, final String style, final String[] options) { this(name, style, options, null); }
Constructor.
Params:
  • name – Name of converter.
  • style – CSS style for output.
  • options – options, may be null.
  • config –
/** * Constructor. * @param name Name of converter. * @param style CSS style for output. * @param options options, may be null. * @param config */
protected ThrowablePatternConverter(final String name, final String style, final String[] options, final Configuration config) { super(name, style); this.options = ThrowableFormatOptions.newInstance(options); if (options != null && options.length > 0) { rawOption = options[0]; } if (this.options.getSuffix() != null) { final PatternParser parser = PatternLayout.createPatternParser(config); final List<PatternFormatter> parsedSuffixFormatters = parser.parse(this.options.getSuffix()); // filter out nested formatters that will handle throwable boolean hasThrowableSuffixFormatter = false; for (final PatternFormatter suffixFormatter : parsedSuffixFormatters) { if (suffixFormatter.handlesThrowable()) { hasThrowableSuffixFormatter = true; } } if (!hasThrowableSuffixFormatter) { this.formatters = parsedSuffixFormatters; } else { final List<PatternFormatter> suffixFormatters = new ArrayList<>(); for (final PatternFormatter suffixFormatter : parsedSuffixFormatters) { if (!suffixFormatter.handlesThrowable()) { suffixFormatters.add(suffixFormatter); } } this.formatters = suffixFormatters; } } else { this.formatters = Collections.emptyList(); } subShortOption = ThrowableFormatOptions.MESSAGE.equalsIgnoreCase(rawOption) || ThrowableFormatOptions.LOCALIZED_MESSAGE.equalsIgnoreCase(rawOption) || ThrowableFormatOptions.FILE_NAME.equalsIgnoreCase(rawOption) || ThrowableFormatOptions.LINE_NUMBER.equalsIgnoreCase(rawOption) || ThrowableFormatOptions.METHOD_NAME.equalsIgnoreCase(rawOption) || ThrowableFormatOptions.CLASS_NAME.equalsIgnoreCase(rawOption); nonStandardLineSeparator = !Strings.LINE_SEPARATOR.equals(this.options.getSeparator()); }
Gets an instance of the class.
Params:
  • config –
  • options – pattern options, may be null. If first element is "short", only the first line of the throwable will be formatted.
Returns:instance of class.
/** * Gets an instance of the class. * * @param config * @param options pattern options, may be null. If first element is "short", * only the first line of the throwable will be formatted. * @return instance of class. */
public static ThrowablePatternConverter newInstance(final Configuration config, final String[] options) { return new ThrowablePatternConverter("Throwable", "throwable", options, config); }
{@inheritDoc}
/** * {@inheritDoc} */
@Override public void format(final LogEvent event, final StringBuilder buffer) { final Throwable t = event.getThrown(); if (subShortOption) { formatSubShortOption(t, getSuffix(event), buffer); } else if (t != null && options.anyLines()) { formatOption(t, getSuffix(event), buffer); } } private void formatSubShortOption(final Throwable t, final String suffix, final StringBuilder buffer) { StackTraceElement[] trace; StackTraceElement throwingMethod = null; int len; if (t != null) { trace = t.getStackTrace(); if (trace !=null && trace.length > 0) { throwingMethod = trace[0]; } } if (t != null && throwingMethod != null) { String toAppend = Strings.EMPTY; if (ThrowableFormatOptions.CLASS_NAME.equalsIgnoreCase(rawOption)) { toAppend = throwingMethod.getClassName(); } else if (ThrowableFormatOptions.METHOD_NAME.equalsIgnoreCase(rawOption)) { toAppend = throwingMethod.getMethodName(); } else if (ThrowableFormatOptions.LINE_NUMBER.equalsIgnoreCase(rawOption)) { toAppend = String.valueOf(throwingMethod.getLineNumber()); } else if (ThrowableFormatOptions.MESSAGE.equalsIgnoreCase(rawOption)) { toAppend = t.getMessage(); } else if (ThrowableFormatOptions.LOCALIZED_MESSAGE.equalsIgnoreCase(rawOption)) { toAppend = t.getLocalizedMessage(); } else if (ThrowableFormatOptions.FILE_NAME.equalsIgnoreCase(rawOption)) { toAppend = throwingMethod.getFileName(); } len = buffer.length(); if (len > 0 && !Character.isWhitespace(buffer.charAt(len - 1))) { buffer.append(' '); } buffer.append(toAppend); if (Strings.isNotBlank(suffix)) { buffer.append(' '); buffer.append(suffix); } } } private void formatOption(final Throwable throwable, final String suffix, final StringBuilder buffer) { final int len = buffer.length(); if (len > 0 && !Character.isWhitespace(buffer.charAt(len - 1))) { buffer.append(' '); } if (!options.allLines() || nonStandardLineSeparator || Strings.isNotBlank(suffix)) { final StringWriter w = new StringWriter(); throwable.printStackTrace(new PrintWriter(w)); final String[] array = w.toString().split(Strings.LINE_SEPARATOR); final int limit = options.minLines(array.length) - 1; final boolean suffixNotBlank = Strings.isNotBlank(suffix); for (int i = 0; i <= limit; ++i) { buffer.append(array[i]); if (suffixNotBlank) { buffer.append(' '); buffer.append(suffix); } if (i < limit) { buffer.append(options.getSeparator()); } } } else { throwable.printStackTrace(new PrintWriter(new StringBuilderWriter(buffer))); } }
This converter obviously handles throwables.
Returns:true.
/** * This converter obviously handles throwables. * * @return true. */
@Override public boolean handlesThrowable() { return true; } protected String getSuffix(final LogEvent event) { if (formatters.isEmpty()) { return Strings.EMPTY; } //noinspection ForLoopReplaceableByForEach final StringBuilder toAppendTo = new StringBuilder(); for (int i = 0, size = formatters.size(); i < size; i++) { formatters.get(i).format(event, toAppendTo); } return toAppendTo.toString(); } public ThrowableFormatOptions getOptions() { return options; } }