| /* |
| * 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.impl; |
| |
| import org.apache.logging.log4j.core.helpers.Constants; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Scanner; |
| |
| /** |
| * Contains options which control how a {@code throwable} pattern is formatted. |
| */ |
| public final class ThrowableFormatOptions { |
| |
| /** |
| * Default instance of {@code ThrowableFormatOptions}. |
| */ |
| protected static final ThrowableFormatOptions DEFAULT = new ThrowableFormatOptions(); |
| |
| /** |
| * Format the whole stack trace. |
| */ |
| private static final String FULL = "full"; |
| |
| /** |
| * Do not format the exception. |
| */ |
| private static final String NONE = "none"; |
| |
| /** |
| * Format only the first line of the throwable. |
| */ |
| private static final String SHORT = "short"; |
| |
| /** |
| * The number of lines to write. |
| */ |
| private final int lines; |
| |
| /** |
| * The stack trace separator. |
| */ |
| private final String separator; |
| |
| /** |
| * The list of packages to filter. |
| */ |
| private final List<String> packages; |
| |
| /** |
| * Construct the options for printing stack trace. |
| * @param lines The number of lines. |
| * @param separator The stack trace separator. |
| * @param packages The packages to filter. |
| */ |
| protected ThrowableFormatOptions(final Integer lines, final String separator, final List<String> packages) { |
| this.lines = lines == null ? Integer.MAX_VALUE : lines; |
| this.separator = separator == null ? Constants.LINE_SEP : separator; |
| this.packages = packages; |
| } |
| |
| /** |
| * Construct the options for printing stack trace. |
| * @param packages The packages to filter. |
| */ |
| protected ThrowableFormatOptions(final List<String> packages) { |
| this(null, null, packages); |
| } |
| |
| /** |
| * Construct the options for printing stack trace. |
| */ |
| protected ThrowableFormatOptions() { |
| this(null, null, null); |
| } |
| |
| /** |
| * Returns the number of lines to write. |
| * @return The number of lines to write. |
| */ |
| public int getLines() { |
| return this.lines; |
| } |
| |
| /** |
| * Returns the stack trace separator. |
| * @return The stack trace separator. |
| */ |
| public String getSeparator() { |
| return this.separator; |
| } |
| |
| /** |
| * Returns the list of packages to filter. |
| * @return The list of packages to filter. |
| */ |
| public List<String> getPackages() { |
| return this.packages; |
| } |
| |
| /** |
| * Determines if all lines should be printed. |
| * @return true for all lines, false otherwise. |
| */ |
| public boolean allLines() { |
| return this.lines == Integer.MAX_VALUE; |
| } |
| |
| /** |
| * Determines if any lines should be printed. |
| * @return true for any lines, false otherwise. |
| */ |
| public boolean anyLines() { |
| return this.lines > 0; |
| } |
| |
| /** |
| * Returns the minimum between the lines and the max lines. |
| * @param maxLines The maximum number of lines. |
| * @return The number of lines to print. |
| */ |
| public int minLines(final int maxLines) { |
| return this.lines > maxLines ? maxLines : this.lines; |
| } |
| |
| /** |
| * Determines if there are any packages to filter. |
| * @return true if there are packages, false otherwise. |
| */ |
| public boolean hasPackages() { |
| return this.packages != null && !this.packages.isEmpty(); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public String toString() { |
| StringBuilder s = new StringBuilder(); |
| s.append("{").append(allLines() ? FULL : this.lines == 2 ? SHORT : anyLines() ? String.valueOf(this.lines) : NONE).append("}"); |
| s.append("{separator(").append(this.separator).append(")}"); |
| if (hasPackages()) { |
| s.append("{filters("); |
| for (String p : this.packages) { |
| s.append(p).append(","); |
| } |
| s.deleteCharAt(s.length() - 1); |
| s.append(")}"); |
| } |
| return s.toString(); |
| } |
| |
| /** |
| * Create a new instance based on the array of options. |
| * @param options The array of options. |
| */ |
| public static ThrowableFormatOptions newInstance(String[] options) { |
| if (options == null || options.length == 0) { |
| return DEFAULT; |
| } else { |
| // NOTE: The following code is present for backward compatibility |
| // and was copied from Extended/RootThrowablePatternConverter. |
| // This supports a single option with the format: |
| // %xEx{["none"|"short"|"full"|depth],[filters(packages)} |
| // However, the convention for multiple options should be: |
| // %xEx{["none"|"short"|"full"|depth]}[{filters(packages)}] |
| if (options.length == 1 && options[0] != null && options[0].length() > 0) { |
| final String[] opts = options[0].split(",", 2); |
| final String first = opts[0].trim(); |
| final Scanner scanner = new Scanner(first); |
| if (opts.length > 1 && (first.equalsIgnoreCase(FULL) || first.equalsIgnoreCase(SHORT) || first.equalsIgnoreCase(NONE) || scanner.hasNextInt())) { |
| options = new String[]{first, opts[1].trim()}; |
| } |
| } |
| |
| int lines = DEFAULT.lines; |
| String separator = DEFAULT.separator; |
| List<String> packages = DEFAULT.packages; |
| for (int i = 0; i < options.length; i++) { |
| if (options[i] != null) { |
| final String option = options[i].trim(); |
| if (option.length() == 0) { |
| // continue; |
| } else if (option.startsWith("separator(") && option.endsWith(")")) { |
| separator = option.substring("separator(".length(), option.length() - 1); |
| } else if (option.startsWith("filters(") && option.endsWith(")")) { |
| final String filterStr = option.substring("filters(".length(), option.length() - 1); |
| if (filterStr.length() > 0) { |
| final String[] array = filterStr.split(","); |
| if (array.length > 0) { |
| packages = new ArrayList<String>(array.length); |
| for (String token : array) { |
| token = token.trim(); |
| if (token.length() > 0) { |
| packages.add(token); |
| } |
| } |
| } |
| } |
| } else if (option.equalsIgnoreCase(NONE)) { |
| lines = 0; |
| } else if (option.equalsIgnoreCase(SHORT)) { |
| lines = 2; |
| } else if (!option.equalsIgnoreCase(FULL)) { |
| lines = Integer.parseInt(option); |
| } |
| } |
| } |
| return new ThrowableFormatOptions(lines, separator, packages); |
| } |
| } |
| } |