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}