
This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "Logback: the generic, reliable, fast and flexible logging framework.". The branch, master has been updated via f0b1a778e9000cadf586790d670cb75ad36bdf3a (commit) from 7a2e02bca24d872d580694ab89a865542103e570 (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- http://git.qos.ch/gitweb/?p=logback.git;a=commit;h=f0b1a778e9000cadf586790d6... http://github.com/ceki/logback/commit/f0b1a778e9000cadf586790d670cb75ad36bdf... commit f0b1a778e9000cadf586790d670cb75ad36bdf3a Author: Tomasz Nurkiewicz <nurkiewicz@gmail.com> Date: Mon Aug 16 01:31:43 2010 +0800 [LBCLASSIC-217] Exception stack trace printing starting from root cause diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/PatternLayout.java b/logback-classic/src/main/java/ch/qos/logback/classic/PatternLayout.java index 9cab8e7..ef32df3 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/PatternLayout.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/PatternLayout.java @@ -35,6 +35,7 @@ import ch.qos.logback.classic.pattern.MessageConverter; import ch.qos.logback.classic.pattern.MethodOfCallerConverter; import ch.qos.logback.classic.pattern.NopThrowableInformationConverter; import ch.qos.logback.classic.pattern.RelativeTimeConverter; +import ch.qos.logback.classic.pattern.RootCauseFirstThrowableProxyConverter; import ch.qos.logback.classic.pattern.ThreadConverter; import ch.qos.logback.classic.pattern.ThrowableProxyConverter; import ch.qos.logback.classic.spi.ILoggingEvent; @@ -100,6 +101,9 @@ public class PatternLayout extends PatternLayoutBase<ILoggingEvent> { defaultConverterMap.put("ex", ThrowableProxyConverter.class.getName()); defaultConverterMap.put("exception", ThrowableProxyConverter.class .getName()); + defaultConverterMap.put("rEx", RootCauseFirstThrowableProxyConverter.class.getName()); + defaultConverterMap.put("rootException", RootCauseFirstThrowableProxyConverter.class + .getName()); defaultConverterMap.put("throwable", ThrowableProxyConverter.class .getName()); diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/RootCauseFirstThrowableProxyConverter.java b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/RootCauseFirstThrowableProxyConverter.java new file mode 100644 index 0000000..3ed88e4 --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/RootCauseFirstThrowableProxyConverter.java @@ -0,0 +1,54 @@ +package ch.qos.logback.classic.pattern; + +import ch.qos.logback.classic.pattern.ExtendedThrowableProxyConverter; +import ch.qos.logback.classic.spi.IThrowableProxy; +import ch.qos.logback.classic.spi.StackTraceElementProxy; +import ch.qos.logback.classic.spi.ThrowableProxyUtil; +import ch.qos.logback.core.CoreConstants; + +/** + * @author Tomasz Nurkiewicz + * @since 2010-08-07, 14:07:10 + */ +public class RootCauseFirstThrowableProxyConverter extends ExtendedThrowableProxyConverter { + + @Override + protected String printThrowableToString(IThrowableProxy tp) { + StringBuilder buf = new StringBuilder(2048); + printRootCauseFirst(tp, buf); + return buf.toString(); + } + + private void printRootCauseFirst(IThrowableProxy tp, StringBuilder buf) { + if (tp.getCause() != null) + printRootCauseFirst(tp.getCause(), buf); + printRootCause(tp, buf); + } + + private void printRootCause(IThrowableProxy tp, StringBuilder buf) { + ThrowableProxyUtil.printFirstLineRootCauseFirst(buf, tp); + buf.append(CoreConstants.LINE_SEPARATOR); + StackTraceElementProxy[] stepArray = tp.getStackTraceElementProxyArray(); + int commonFrames = tp.getCommonFrames(); + + boolean unrestrictedPrinting = lengthOption > stepArray.length; + int length = (unrestrictedPrinting) ? stepArray.length : lengthOption; + + + int maxIndex = length; + if (commonFrames > 0 && unrestrictedPrinting) { + maxIndex -= commonFrames; + } + + for (int i = 0; i < maxIndex; i++) { + String string = stepArray[i].toString(); + buf.append(CoreConstants.TAB); + buf.append(string); + extraData(buf, stepArray[i]); // allow other data to be added + buf.append(CoreConstants.LINE_SEPARATOR); + } + + } + + +} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ThrowableProxyConverter.java b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ThrowableProxyConverter.java index 0a68cf8..3ddb1e5 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ThrowableProxyConverter.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ThrowableProxyConverter.java @@ -96,7 +96,6 @@ public class ThrowableProxyConverter extends ThrowableHandlingConverter { } public String convert(ILoggingEvent event) { - StringBuilder buf = new StringBuilder(32); IThrowableProxy tp = event.getThrowableProxy(); if (tp == null) { @@ -135,10 +134,16 @@ public class ThrowableProxyConverter extends ThrowableHandlingConverter { } } - while (tp != null) { - printThrowableProxy(buf, tp); - tp = tp.getCause(); + return printThrowableToString(tp); } + + protected String printThrowableToString(IThrowableProxy tp) { + StringBuilder buf = new StringBuilder(32); + IThrowableProxy currentThrowable = tp; + while (currentThrowable != null) { + printThrowableProxy(buf, currentThrowable); + currentThrowable = currentThrowable.getCause(); + } return buf.toString(); } @@ -166,7 +171,7 @@ public class ThrowableProxyConverter extends ThrowableHandlingConverter { } if (commonFrames > 0 && unrestrictedPrinting) { - buf.append("\t... " + tp.getCommonFrames()).append( + buf.append("\t... ").append(tp.getCommonFrames()).append( " common frames omitted").append(CoreConstants.LINE_SEPARATOR); } } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/spi/ThrowableProxyUtil.java b/logback-classic/src/main/java/ch/qos/logback/classic/spi/ThrowableProxyUtil.java index c02cadd..1283bce 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/spi/ThrowableProxyUtil.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/spi/ThrowableProxyUtil.java @@ -117,7 +117,7 @@ public class ThrowableProxyUtil { } if (commonFrames > 0) { - sb.append("\t... " + commonFrames).append(" common frames omitted") + sb.append("\t... ").append(commonFrames).append(" common frames omitted") .append(CoreConstants.LINE_SEPARATOR); } @@ -128,6 +128,17 @@ public class ThrowableProxyUtil { if (commonFrames > 0) { buf.append(CoreConstants.CAUSED_BY); } + printExceptionMessage(buf, tp); + } + + static public void printFirstLineRootCauseFirst(StringBuilder buf, IThrowableProxy tp) { + if (tp.getCause() != null) { + buf.append(CoreConstants.WRAPPED_BY); + } + printExceptionMessage(buf, tp); + } + + private static void printExceptionMessage(StringBuilder buf, IThrowableProxy tp) { buf.append(tp.getClassName()).append(": ").append(tp.getMessage()); } } diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/pattern/RootCauseFirstThrowableProxyConverterTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/RootCauseFirstThrowableProxyConverterTest.java new file mode 100644 index 0000000..ccd0873 --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/RootCauseFirstThrowableProxyConverterTest.java @@ -0,0 +1,103 @@ +package ch.qos.logback.classic.pattern; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.PatternLayout; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.spi.LoggingEvent; +import org.junit.Before; +import org.junit.Test; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static ch.qos.logback.classic.util.TeztHelper.makeNestedException; +import static ch.qos.logback.classic.util.TeztHelper.positionOf; +import static org.fest.assertions.Assertions.assertThat; + +/** + * @author Tomasz Nurkiewicz + * @since 2010-08-15, 18:34:21 + */ +public class RootCauseFirstThrowableProxyConverterTest { + + private LoggerContext context = new LoggerContext(); + private ThrowableProxyConverter converter = new RootCauseFirstThrowableProxyConverter(); + private StringWriter stringWriter = new StringWriter(); + private PrintWriter printWriter = new PrintWriter(stringWriter); + + @Before + public void setUp() throws Exception { + converter.setContext(context); + converter.start(); + } + + private ILoggingEvent createLoggingEvent(Throwable t) { + return new LoggingEvent(this.getClass().getName(), context + .getLogger(Logger.ROOT_LOGGER_NAME), Level.DEBUG, "test message", t, + null); + } + + @Test + public void integration() { + //given + PatternLayout pl = new PatternLayout(); + pl.setContext(context); + pl.setPattern("%m%rEx%n"); + pl.start(); + + //when + ILoggingEvent e = createLoggingEvent(new Exception("x")); + String result = pl.doLayout(e); + + //then + // make sure that at least some package data was output + Pattern p = Pattern.compile(" \\[junit.*\\]"); + Matcher m = p.matcher(result); + int i = 0; + while(m.find()) { + i++; + } + assertThat(i).isGreaterThan(5); + } + + @Test + public void smoke() { + //given + Exception exception = new Exception("smoke"); + exception.printStackTrace(printWriter); + + //when + ILoggingEvent le = createLoggingEvent(exception); + String result = converter.convert(le); + + //then + result = result.replace("common frames omitted", "more"); + result = result.replaceAll(" ~?\\[.*\\]", ""); + assertThat(result).isEqualTo(stringWriter.toString()); + } + + @Test + public void nested() { + //given + Throwable nestedException = makeNestedException(2); + nestedException.printStackTrace(printWriter); + + //when + ILoggingEvent le = createLoggingEvent(nestedException); + String result = converter.convert(le); + + //then + assertThat(result).startsWith("java.lang.Exception: nesting level=0"); + assertThat( + positionOf("nesting level=0").in(result)).isLessThan( + positionOf("nesting level =1").in(result)); + assertThat( + positionOf("nesting level =1").in(result)).isLessThan( + positionOf("nesting level =2").in(result)); + } + +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/util/TeztHelper.java b/logback-classic/src/test/java/ch/qos/logback/classic/util/TeztHelper.java index f9f848d..badde04 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/util/TeztHelper.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/util/TeztHelper.java @@ -23,4 +23,35 @@ public class TeztHelper { Throwable cause = makeNestedException(level - 1); return new Exception("nesting level =" + level, cause); } + + /** + * Usage: + * <pre> + * String s = "123"; + * positionOf("1").in(s) < positionOf("3").in(s) + * </pre> + * + * @param pattern Plain text to be found + * @return StringPosition fluent interface + */ + public static StringPosition positionOf(String pattern) { + return new StringPosition(pattern); + } + + public static class StringPosition { + private final String pattern; + + public StringPosition(String pattern) { + this.pattern = pattern; + } + + public int in(String s) { + final int position = s.indexOf(pattern); + if(position < 0) + throw new IllegalArgumentException("String '" + pattern + "' not found in: '" + s + "'"); + return position; + } + + } + } ----------------------------------------------------------------------- Summary of changes: .../java/ch/qos/logback/classic/PatternLayout.java | 4 + .../RootCauseFirstThrowableProxyConverter.java | 54 ++++++++++ .../classic/pattern/ThrowableProxyConverter.java | 15 ++- .../logback/classic/spi/ThrowableProxyUtil.java | 13 +++- .../RootCauseFirstThrowableProxyConverterTest.java | 103 ++++++++++++++++++++ .../ch/qos/logback/classic/util/TeztHelper.java | 31 ++++++ 6 files changed, 214 insertions(+), 6 deletions(-) create mode 100644 logback-classic/src/main/java/ch/qos/logback/classic/pattern/RootCauseFirstThrowableProxyConverter.java create mode 100644 logback-classic/src/test/java/ch/qos/logback/classic/pattern/RootCauseFirstThrowableProxyConverterTest.java hooks/post-receive -- Logback: the generic, reliable, fast and flexible logging framework.