001/**
002 * Logback: the reliable, generic, fast and flexible logging framework.
003 * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
004 *
005 * This program and the accompanying materials are dual-licensed under
006 * either the terms of the Eclipse Public License v1.0 as published by
007 * the Eclipse Foundation
008 *
009 *   or (per the licensee's choosing)
010 *
011 * under the terms of the GNU Lesser General Public License version 2.1
012 * as published by the Free Software Foundation.
013 */
014package ch.qos.logback.classic.pattern;
015
016import ch.qos.logback.classic.LoggerContext;
017import ch.qos.logback.classic.spi.ILoggingEvent;
018import ch.qos.logback.core.Context;
019import ch.qos.logback.core.pattern.CompositeConverter;
020import ch.qos.logback.core.pattern.Converter;
021import ch.qos.logback.core.pattern.ConverterUtil;
022import ch.qos.logback.core.pattern.PostCompileProcessor;
023
024public class EnsureExceptionHandling implements PostCompileProcessor<ILoggingEvent> {
025
026    /**
027     * This implementation checks if any of the converters in the chain handles
028     * exceptions. If not, then this method adds a
029     * {@link ExtendedThrowableProxyConverter} instance to the end of the chain.
030     * <p>
031     * This allows appenders using this layout to output exception information event
032     * if the user forgets to add %ex to the pattern. Note that the appenders
033     * defined in the Core package are not aware of exceptions nor LoggingEvents.
034     * <p>
035     * If for some reason the user wishes to NOT print exceptions, then she can add
036     * %nopex to the pattern.
037     * 
038     * 
039     */
040    public void process(Context context, Converter<ILoggingEvent> head) {
041        if (head == null) {
042            // this should never happen
043            throw new IllegalArgumentException("cannot process empty chain");
044        }
045        if (!chainHandlesThrowable(head)) {
046            Converter<ILoggingEvent> tail = ConverterUtil.findTail(head);
047            Converter<ILoggingEvent> exConverter = null;
048            LoggerContext loggerContext = (LoggerContext) context;
049            if (loggerContext.isPackagingDataEnabled()) {
050                exConverter = new ExtendedThrowableProxyConverter();
051            } else {
052                exConverter = new ThrowableProxyConverter();
053            }
054            tail.setNext(exConverter);
055        }
056    }
057
058    /**
059     * This method computes whether a chain of converters handles exceptions or not.
060     * 
061     * @param head The first element of the chain
062     * @return true if it can handle throwables contained in logging events
063     */
064    public boolean chainHandlesThrowable(Converter<ILoggingEvent> head) {
065        Converter<ILoggingEvent> c = head;
066        while (c != null) {
067            if (c instanceof ThrowableHandlingConverter) {
068                return true;
069            } else if (c instanceof CompositeConverter) {
070                if (compositeHandlesThrowable((CompositeConverter<ILoggingEvent>) c)) {
071                    return true;
072                }
073            }
074            c = c.getNext();
075        }
076        return false;
077    }
078
079    /**
080     * This method computes whether a composite converter handles exceptions or not.
081     *
082     * @param compositeConverter The composite converter
083     * @return true if it can handle throwables contained in logging events
084     */
085    public boolean compositeHandlesThrowable(CompositeConverter<ILoggingEvent> compositeConverter) {
086        Converter<ILoggingEvent> childConverter = compositeConverter.getChildConverter();
087
088        for (Converter<ILoggingEvent> c = childConverter; c != null; c = c.getNext()) {
089            if (c instanceof ThrowableHandlingConverter) {
090                return true;
091            } else if (c instanceof CompositeConverter) {
092                boolean r = compositeHandlesThrowable((CompositeConverter<ILoggingEvent>) c);
093                if (r)
094                    return true;
095            }
096
097        }
098        return false;
099    }
100}