
Author: ceki Date: Fri Sep 5 19:14:47 2008 New Revision: 1794 Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ExtendedThrowableProxyConverter.java logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ThrowableProxyConverter.java - copied, changed from r1793, /logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ThrowableInformationConverter.java logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/spi/PackageInfo.java logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/spi/PackageInfoCalculator.java logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/spi/PlatformInfo.java - copied, changed from r1772, /logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/helpers/PlatformInfo.java logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/spi/STEUtil.java logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/spi/StackTraceElementProxy.java logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/spi/ThrowableDataPoint.java logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/spi/ThrowableToDataPointArray.java logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/ExtendedThrowableProxyConverterTest.java logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/ThrowableProxyConverterTest.java logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/spi/PackageInfoTest.java logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/spi/PackageVersionCalculatorTest.java logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/spi/ThrowableToDataPointTest.java logback/trunk/logback-core/src/test/java/ch/qos/logback/core/helpers/PackageTest.java logback/trunk/logback-core/src/test/java/ch/qos/logback/core/helpers/ThrowableToStringArrayTest.java Removed: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/helpers/PlatformInfo.java logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ThrowableInformationConverter.java logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/UtilTest.java logback/trunk/logback-core/src/main/java/ch/qos/logback/core/helpers/PackageInfo.java logback/trunk/logback-core/src/main/java/ch/qos/logback/core/helpers/StackTraceElementProxy.java logback/trunk/logback-core/src/main/java/ch/qos/logback/core/helpers/ThrowableDataPoint.java logback/trunk/logback-core/src/main/java/ch/qos/logback/core/helpers/ThrowableToDataPointArray.java logback/trunk/logback-core/src/test/java/ch/qos/logback/core/helpers/ThrowableToDataPointTest.java Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/PatternLayout.java logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/DBAppender.java logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/DBHelper.java logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/html/DefaultThrowableRenderer.java logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/joran/JoranConfigurator.java logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/net/SyslogAppender.java logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/EnsureExceptionHandling.java logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/Util.java logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/spi/ThrowableProxy.java logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/AllClassicTest.java logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/html/HTMLLayoutTest.java logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/ConverterTest.java logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/LRUCacheTest.java logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/spi/PackageTest.java logback/trunk/logback-core/src/main/java/ch/qos/logback/core/helpers/ThrowableToStringArray.java logback/trunk/logback-core/src/main/java/ch/qos/logback/core/util/StatusPrinter.java logback/trunk/logback-core/src/test/java/ch/qos/logback/core/AllCoreTest.java Log: LBGENERAL-23 Working on the reliability of the extracted package information which is actually quite a difficult problem. Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/PatternLayout.java ============================================================================== --- logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/PatternLayout.java (original) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/PatternLayout.java Fri Sep 5 19:14:47 2008 @@ -28,7 +28,7 @@ import ch.qos.logback.classic.pattern.NopThrowableInformationConverter; import ch.qos.logback.classic.pattern.RelativeTimeConverter; import ch.qos.logback.classic.pattern.ThreadConverter; -import ch.qos.logback.classic.pattern.ThrowableInformationConverter; +import ch.qos.logback.classic.pattern.ThrowableProxyConverter; import ch.qos.logback.classic.spi.LoggingEvent; import ch.qos.logback.core.CoreGlobal; import ch.qos.logback.core.pattern.PatternLayoutBase; @@ -90,10 +90,10 @@ defaultConverterMap.put("mdc", MDCConverter.class.getName()); defaultConverterMap - .put("ex", ThrowableInformationConverter.class.getName()); - defaultConverterMap.put("exception", ThrowableInformationConverter.class + .put("ex", ThrowableProxyConverter.class.getName()); + defaultConverterMap.put("exception", ThrowableProxyConverter.class .getName()); - defaultConverterMap.put("throwable", ThrowableInformationConverter.class + defaultConverterMap.put("throwable", ThrowableProxyConverter.class .getName()); defaultConverterMap.put("nopex", NopThrowableInformationConverter.class Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/DBAppender.java ============================================================================== --- logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/DBAppender.java (original) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/DBAppender.java Fri Sep 5 19:14:47 2008 @@ -21,8 +21,8 @@ import ch.qos.logback.classic.spi.CallerData; import ch.qos.logback.classic.spi.LoggingEvent; +import ch.qos.logback.classic.spi.ThrowableDataPoint; import ch.qos.logback.core.db.DBAppenderBase; -import ch.qos.logback.core.helpers.ThrowableDataPoint; /** * The DBAppender inserts logging events into three database tables in a format Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/DBHelper.java ============================================================================== --- logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/DBHelper.java (original) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/DBHelper.java Fri Sep 5 19:14:47 2008 @@ -11,7 +11,7 @@ package ch.qos.logback.classic.db; import ch.qos.logback.classic.spi.LoggingEvent; -import ch.qos.logback.core.helpers.ThrowableDataPoint; +import ch.qos.logback.classic.spi.ThrowableDataPoint; /** * @author Ceki Gülcü Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/html/DefaultThrowableRenderer.java ============================================================================== --- logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/html/DefaultThrowableRenderer.java (original) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/html/DefaultThrowableRenderer.java Fri Sep 5 19:14:47 2008 @@ -3,8 +3,8 @@ import static ch.qos.logback.core.Layout.LINE_SEP; import ch.qos.logback.classic.helpers.Transform; import ch.qos.logback.classic.spi.LoggingEvent; +import ch.qos.logback.classic.spi.ThrowableDataPoint; import ch.qos.logback.classic.spi.ThrowableProxy; -import ch.qos.logback.core.helpers.ThrowableDataPoint; import ch.qos.logback.core.html.IThrowableRenderer; public class DefaultThrowableRenderer implements IThrowableRenderer { Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/joran/JoranConfigurator.java ============================================================================== --- logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/joran/JoranConfigurator.java (original) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/joran/JoranConfigurator.java Fri Sep 5 19:14:47 2008 @@ -10,7 +10,6 @@ package ch.qos.logback.classic.joran; -import ch.qos.logback.classic.helpers.PlatformInfo; import ch.qos.logback.classic.joran.action.ConfigurationAction; import ch.qos.logback.classic.joran.action.ConsolePluginAction; import ch.qos.logback.classic.joran.action.EvaluatorAction; @@ -19,6 +18,7 @@ import ch.qos.logback.classic.joran.action.LevelAction; import ch.qos.logback.classic.joran.action.LoggerAction; import ch.qos.logback.classic.joran.action.RootLoggerAction; +import ch.qos.logback.classic.spi.PlatformInfo; import ch.qos.logback.core.joran.JoranConfiguratorBase; import ch.qos.logback.core.joran.action.AppenderRefAction; import ch.qos.logback.core.joran.action.IncludeAction; Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/net/SyslogAppender.java ============================================================================== --- logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/net/SyslogAppender.java (original) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/net/SyslogAppender.java Fri Sep 5 19:14:47 2008 @@ -14,9 +14,9 @@ import ch.qos.logback.classic.PatternLayout; import ch.qos.logback.classic.pattern.SyslogStartConverter; import ch.qos.logback.classic.spi.LoggingEvent; +import ch.qos.logback.classic.spi.ThrowableDataPoint; import ch.qos.logback.classic.util.LevelToSyslogSeverity; import ch.qos.logback.core.Layout; -import ch.qos.logback.core.helpers.ThrowableDataPoint; import ch.qos.logback.core.net.SyslogAppenderBase; import ch.qos.logback.core.net.SyslogWriter; Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/EnsureExceptionHandling.java ============================================================================== --- logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/EnsureExceptionHandling.java (original) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/EnsureExceptionHandling.java Fri Sep 5 19:14:47 2008 @@ -30,7 +30,7 @@ public void process(Converter<LoggingEvent> head) { if (!chainHandlesThrowable(head)) { Converter<LoggingEvent> tail = ConverterUtil.findTail(head); - Converter<LoggingEvent> exConverter = new ThrowableInformationConverter(); + Converter<LoggingEvent> exConverter = new ThrowableProxyConverter(); if (tail == null) { head = exConverter; } else { Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ExtendedThrowableProxyConverter.java ============================================================================== --- (empty file) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ExtendedThrowableProxyConverter.java Fri Sep 5 19:14:47 2008 @@ -0,0 +1,29 @@ +/** + * Logback: the generic, reliable, fast and flexible logging framework. + * + * Copyright (C) 2000-2008, QOS.ch + * + * This library is free software, you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation. + */ +package ch.qos.logback.classic.pattern; + +import ch.qos.logback.classic.spi.PackageInfo; +import ch.qos.logback.classic.spi.StackTraceElementProxy; +import ch.qos.logback.classic.spi.ThrowableDataPoint; + + +public class ExtendedThrowableProxyConverter extends ThrowableProxyConverter { + + @Override + protected void extraData(StringBuilder builder, ThrowableDataPoint tdp) { + StackTraceElementProxy step = tdp.getStackTraceElementProxy(); + if(step != null) { + PackageInfo pi = step.getPackageInfo(); + if(pi != null) { + builder.append(" [").append(pi.getJarName()).append(':').append(pi.getVersion()).append(']'); + } + } + } +} Copied: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ThrowableProxyConverter.java (from r1793, /logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ThrowableInformationConverter.java) ============================================================================== --- /logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ThrowableInformationConverter.java (original) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ThrowableProxyConverter.java Fri Sep 5 19:14:47 2008 @@ -14,20 +14,20 @@ import java.util.Map; import ch.qos.logback.classic.spi.LoggingEvent; +import ch.qos.logback.classic.spi.ThrowableDataPoint; import ch.qos.logback.classic.spi.ThrowableProxy; import ch.qos.logback.core.Context; import ch.qos.logback.core.CoreGlobal; import ch.qos.logback.core.boolex.EvaluationException; import ch.qos.logback.core.boolex.EventEvaluator; -import ch.qos.logback.core.helpers.ThrowableDataPoint; import ch.qos.logback.core.status.ErrorStatus; /** - * Add a stack trace i case the event contains a Throwable. + * Add a stack trace in case the event contains a Throwable. * * @author Ceki Gülcü */ -public class ThrowableInformationConverter extends ThrowableHandlingConverter { +public class ThrowableProxyConverter extends ThrowableHandlingConverter { int lengthOption; List<EventEvaluator> evaluatorList = null; @@ -85,8 +85,12 @@ super.stop(); } + protected void extraData(StringBuilder builder, ThrowableDataPoint tdp) { + // nop + } + public String convert(LoggingEvent event) { - StringBuffer buf = new StringBuffer(32); + StringBuilder buf = new StringBuilder(32); ThrowableProxy information = event.getThrowableProxy(); @@ -133,15 +137,9 @@ buf.append(tdpArray[0]).append(CoreGlobal.LINE_SEPARATOR); for (int i = 1; i < length; i++) { String string = tdpArray[i].toString(); - - if (string.startsWith(CoreGlobal.CAUSED_BY)) { - // nothing - } else if (Character.isDigit(string.charAt(0))) { - buf.append("\t... "); - } else { - buf.append("\tat "); - } - buf.append(string).append(CoreGlobal.LINE_SEPARATOR); + buf.append(string); + extraData(buf, tdpArray[i]); // allow other data to be appended + buf.append(CoreGlobal.LINE_SEPARATOR); } return buf.toString(); Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/Util.java ============================================================================== --- logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/Util.java (original) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/Util.java Fri Sep 5 19:14:47 2008 @@ -9,17 +9,15 @@ */ package ch.qos.logback.classic.pattern; -import java.net.URL; import java.util.HashMap; import java.util.Map; import org.slf4j.Marker; -import ch.qos.logback.core.helpers.PackageInfo; +import ch.qos.logback.classic.spi.PackageInfo; /** * - * @author James Strachan * @author Ceki Gulcu */ public class Util { @@ -44,123 +42,5 @@ return false; } - static String getVersion(String className) { - String packageName = getPackageName(className); - Package aPackage = Package.getPackage(packageName); - if (aPackage != null) { - String v = aPackage.getImplementationVersion(); - if (v == null) { - return "na"; - } else { - return v; - } - } - return "na"; - } - - static public PackageInfo getPackageInfo(String className) { - PackageInfo pi = cache.get(className); - if(pi != null) { - return pi; - } - String version = getVersion(className); - String jarname = getJarNameOfClass(className); - pi = new PackageInfo(jarname, version); - //cache.put(className, pi); - return pi; - } - - static String getPackageName(String className) { - int j = className.lastIndexOf('.'); - return className.substring(0, j); - } - - /** - * Uses the context class path or the current global class loader to deduce - * the file that the given class name comes from - */ - static String getJarNameOfClass0(String className) { - try { - Class type = findClass(className); - if (type != null) { - URL resource = type.getClassLoader().getResource( - type.getName().replace('.', '/') + ".class"); - - - // "jar:file:/C:/java/../repo/groupId/artifact/1.3/artifact-1.3.jar!/com/some/package/Some.class - if (resource != null) { - String text = resource.toString(); - int idx = text.lastIndexOf('!'); - if (idx > 0) { - text = text.substring(0, idx); - // now lets remove all but the file name - idx = text.lastIndexOf('/'); - if (idx > 0) { - text = text.substring(idx + 1); - } - idx = text.lastIndexOf('\\'); - if (idx > 0) { - text = text.substring(idx + 1); - } - return text; - } - } - } - } catch (Exception e) { - // ignore - } - return "na"; - } - - static String getJarNameOfClass1(String className) { - try { - Class type = findClass(className); - if (type != null) { - - - // file:/C:/java/maven-2.0.8/repo/com/icegreen/greenmail/1.3/greenmail-1.3.jar - URL resource = type.getProtectionDomain().getCodeSource().getLocation(); - if (resource != null) { - String text = resource.toString(); - // now lets remove all but the file name - int idx = text.lastIndexOf('/'); - if (idx > 0) { - text = text.substring(idx + 1); - } - idx = text.lastIndexOf('\\'); - if (idx > 0) { - text = text.substring(idx + 1); - } - return text; - } - } - } catch (Exception e) { - // ignore - } - return "na"; - } - - - - static String getJarNameOfClass(String className) { - return getJarNameOfClass1(className); - } - - static private Class findClass(String className) { - try { - return Thread.currentThread().getContextClassLoader() - .loadClass(className); - } catch (ClassNotFoundException e) { - try { - return Class.forName(className); - } catch (ClassNotFoundException e1) { - try { - return Util.class.getClassLoader().loadClass(className); - } catch (ClassNotFoundException e2) { - return null; - } - } - } - } - + } Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/spi/PackageInfo.java ============================================================================== --- (empty file) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/spi/PackageInfo.java Fri Sep 5 19:14:47 2008 @@ -0,0 +1,34 @@ +/** + * Logback: the generic, reliable, fast and flexible logging framework. + * + * Copyright (C) 2000-2008, QOS.ch + * + * This library is free software, you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation. + */ +package ch.qos.logback.classic.spi; + +import java.io.Serializable; + +public class PackageInfo implements Serializable { + + private static final long serialVersionUID = 637783570208674312L; + + String jarName; + String version; + + public PackageInfo(String jarName, String version) { + this.jarName = jarName; + this.version = version; + } + + public String getJarName() { + return jarName; + } + + public String getVersion() { + return version; + } + +} Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/spi/PackageInfoCalculator.java ============================================================================== --- (empty file) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/spi/PackageInfoCalculator.java Fri Sep 5 19:14:47 2008 @@ -0,0 +1,197 @@ +package ch.qos.logback.classic.spi; + +import java.net.URL; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; + +import sun.reflect.Reflection; + +import ch.qos.logback.classic.spi.ThrowableDataPoint.ThrowableDataPointType; + + + +/** + * + * Given a classname locate associated PackageInfo (jar name, version name). + * + * @author James Strachan + * @Ceki Gülcü + */ +public class PackageInfoCalculator { + + final static StackTraceElementProxy[] STEP_ARRAY_TEMPLATE = new StackTraceElementProxy[0]; + + HashMap<String, PackageInfo> cache = new HashMap<String, PackageInfo>(); + + + PackageInfoCalculator() { + + } + + public void computePackageInfo(ThrowableDataPoint[] tdpArray) { + + int steStart= findSTEStartIndex(tdpArray, 0); + StackTraceElementProxy[] stepArray = getSTEPArray(tdpArray, steStart); + populateWithPackageInfo(stepArray); + + } + + void populateWithPackageInfo(StackTraceElementProxy[] stepArray) { + Throwable t = new Throwable("local"); + //t.printStackTrace(); + StackTraceElement[] localteSTEArray = t.getStackTrace(); + int commonFrames = STEUtil.findNumberOfCommonFrames(localteSTEArray, stepArray); + + int localFirstCommon = localteSTEArray.length-commonFrames; + int stepFirstCommon = stepArray.length-commonFrames; + + //System.out.println("commonFr4ames="+commonFrames); + + int missfireCount = 0; + for(int i = 0; i < commonFrames; i++) { + Class callerClass = Reflection.getCallerClass(localFirstCommon+i-missfireCount+1); + StackTraceElementProxy step = stepArray[stepFirstCommon+i]; + String stepClassname = step.ste.getClassName(); + //System.out.println("step.class = "+stepClassname); + + if(!stepClassname.equals(callerClass.getName())) { + missfireCount++; + PackageInfo pi = compute(step); + step.setPackageInfo(pi); + } else { + PackageInfo pi = computeByType(callerClass); + step.setPackageInfo(pi); + } + } + + populateX(commonFrames, stepArray); + + } + + int findSTEStartIndex(ThrowableDataPoint[] tdpArray, int from) { + int len = tdpArray.length; + if(from < 0 || from >= len) { + return -1; + } + for(int i = from; i < len; i++) { + if(tdpArray[i].type == ThrowableDataPointType.STEP) { + return i; + } + } + return -1; + } + + void populateX(int commonFrames, StackTraceElementProxy[] stepArray) { + for(int i = 0; i < commonFrames; i++) { + StackTraceElementProxy step = stepArray[i]; + PackageInfo pi = compute(step); + step.setPackageInfo(pi); + } + } + + StackTraceElementProxy[] getSTEPArray(ThrowableDataPoint[] tdpArray, int from) { + List<StackTraceElementProxy> stepList = new LinkedList<StackTraceElementProxy>(); + int len = tdpArray.length; + if(from < 0 || from >= len) { + return stepList.toArray(STEP_ARRAY_TEMPLATE); + } + for(int i = from; i < len; i++) { + final ThrowableDataPoint tdp = tdpArray[i]; + + if(tdp.type == ThrowableDataPointType.STEP) { + stepList.add(tdp.getStackTraceElementProxy()); + } else { + break; + } + } + return stepList.toArray(STEP_ARRAY_TEMPLATE); + } + + private PackageInfo computeByType(Class type) { + String className = type.getName(); + PackageInfo pi = cache.get(className); + if (pi != null) { + return pi; + } + String version = getVersion(className); + String jarname = getJarNameOfClass(type); + pi = new PackageInfo(jarname, version); + cache.put(className, pi); + return pi; + } + + private PackageInfo compute(StackTraceElementProxy step) { + String className = step.ste.getClassName(); + PackageInfo pi = cache.get(className); + if (pi != null) { + return pi; + } + String version = getVersion(className); + Class type = bestEffortFindClass(className); + String jarname = getJarNameOfClass(type); + pi = new PackageInfo(jarname, version); + cache.put(className, pi); + return pi; + } + + String getVersion(String className) { + String packageName = getPackageName(className); + Package aPackage = Package.getPackage(packageName); + if (aPackage != null) { + String v = aPackage.getImplementationVersion(); + if (v == null) { + return "na"; + } else { + return v; + } + } + return "na"; + } + + static String getPackageName(String className) { + int j = className.lastIndexOf('.'); + return className.substring(0, j); + } + + String getJarNameOfClass(Class type) { + try { + if (type != null) { + + // file:/C:/java/maven-2.0.8/repo/com/icegreen/greenmail/1.3/greenmail-1.3.jar + URL resource = type.getProtectionDomain().getCodeSource().getLocation(); + if (resource != null) { + String text = resource.toString(); + // now lets remove all but the file name + int idx = text.lastIndexOf('/'); + if (idx > 0) { + text = text.substring(idx + 1); + } + idx = text.lastIndexOf('\\'); + if (idx > 0) { + text = text.substring(idx + 1); + } + return text; + } + } + } catch (Exception e) { + // ignore + } + return "na"; + } + + + private Class bestEffortFindClass(String className) { + try { + return Thread.currentThread().getContextClassLoader() + .loadClass(className); + } catch (ClassNotFoundException e) { + try { + return Class.forName(className); + } catch (ClassNotFoundException e1) { + return null; + } + } + } + +} Copied: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/spi/PlatformInfo.java (from r1772, /logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/helpers/PlatformInfo.java) ============================================================================== --- /logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/helpers/PlatformInfo.java (original) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/spi/PlatformInfo.java Fri Sep 5 19:14:47 2008 @@ -1,4 +1,4 @@ -package ch.qos.logback.classic.helpers; +package ch.qos.logback.classic.spi; /** * This class provides information about the runtime platform. Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/spi/STEUtil.java ============================================================================== --- (empty file) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/spi/STEUtil.java Fri Sep 5 19:14:47 2008 @@ -0,0 +1,48 @@ +package ch.qos.logback.classic.spi; + +public class STEUtil { + + + static int findNumberOfCommonFrames(StackTraceElement[] steArray, + StackTraceElement[] otherSTEArray) { + if (otherSTEArray == null) { + return 0; + } + + int steIndex = steArray.length - 1; + int parentIndex = otherSTEArray.length - 1; + int count = 0; + while (steIndex >= 0 && parentIndex >= 0) { + if (steArray[steIndex].equals(otherSTEArray[parentIndex])) { + count++; + } else { + break; + } + steIndex--; + parentIndex--; + } + return count; + } + + + static int findNumberOfCommonFrames(StackTraceElement[] steArray, + StackTraceElementProxy[] otherSTEPArray) { + if (otherSTEPArray == null) { + return 0; + } + + int steIndex = steArray.length - 1; + int parentIndex = otherSTEPArray.length - 1; + int count = 0; + while (steIndex >= 0 && parentIndex >= 0) { + if (steArray[steIndex].equals(otherSTEPArray[parentIndex].ste)) { + count++; + } else { + break; + } + steIndex--; + parentIndex--; + } + return count; + } +} Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/spi/StackTraceElementProxy.java ============================================================================== --- (empty file) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/spi/StackTraceElementProxy.java Fri Sep 5 19:14:47 2008 @@ -0,0 +1,59 @@ +package ch.qos.logback.classic.spi; + +import java.io.Serializable; + + + +public class StackTraceElementProxy implements Serializable { + + private static final long serialVersionUID = -4832130320500439038L; + + final StackTraceElement ste; + private String steAsString; + private PackageInfo pi; + + StackTraceElementProxy(StackTraceElement ste) { + if(ste == null) { + throw new IllegalArgumentException("ste cannot be null"); + } + this.ste = ste; + } + + public String getSTEAsString() { + if(steAsString == null) { + steAsString = "\tat "+ste.toString(); + } + return steAsString; + } + + void setPackageInfo(PackageInfo pi) { + this.pi = pi; + } + + public PackageInfo getPackageInfo() { + return pi; + } + + @Override + public int hashCode() { + return ste.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + final StackTraceElementProxy other = (StackTraceElementProxy) obj; + return ste.equals(other.ste); + } + + @Override + public String toString() { + return getSTEAsString(); + } + +} Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/spi/ThrowableDataPoint.java ============================================================================== --- (empty file) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/spi/ThrowableDataPoint.java Fri Sep 5 19:14:47 2008 @@ -0,0 +1,100 @@ +/** + * Logback: the generic, reliable, fast and flexible logging framework. + * + * Copyright (C) 2000-2008, QOS.ch + * + * This library is free software, you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation. + */ +package ch.qos.logback.classic.spi; + +import java.io.Serializable; + + + +/** + * A container for either raw strings or StackTraceElementProxy instances. + * + * @author Ceki Gülcü + * + */ +public class ThrowableDataPoint implements Serializable { + + private static final long serialVersionUID = -2891376879381358469L; + + public enum ThrowableDataPointType { + RAW, STEP; + } + + String rawString; + StackTraceElementProxy step; + final ThrowableDataPointType type; + + public ThrowableDataPoint(String rawString) { + this.rawString = rawString; + this.type = ThrowableDataPointType.RAW; + } + + public ThrowableDataPoint(StackTraceElement ste) { + this.step = new StackTraceElementProxy(ste); + this.type = ThrowableDataPointType.STEP; + } + + public ThrowableDataPointType getType() { + return type; + } + + public StackTraceElementProxy getStackTraceElementProxy() { + return step; + } + + @Override + public String toString() { + switch (type) { + case RAW: + return rawString; + case STEP: + return step.getSTEAsString(); + } + throw new IllegalStateException("Unreachable code"); + } + + @Override + public int hashCode() { + switch (type) { + case RAW: + return rawString.hashCode(); + case STEP: + return step.hashCode(); + } + throw new IllegalStateException("Unreachable code"); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + final ThrowableDataPoint other = (ThrowableDataPoint) obj; + + switch (type) { + case RAW: + if (rawString == null) { + if (other.rawString != null) + return false; + else + return true; + } else { + return rawString.equals(other.rawString); + } + case STEP: + return step.equals(other.step); + } + throw new IllegalStateException("Unreachable code"); + } + +} Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/spi/ThrowableProxy.java ============================================================================== --- logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/spi/ThrowableProxy.java (original) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/spi/ThrowableProxy.java Fri Sep 5 19:14:47 2008 @@ -11,25 +11,34 @@ import java.util.Arrays; -import ch.qos.logback.core.Layout; -import ch.qos.logback.core.helpers.ThrowableDataPoint; -import ch.qos.logback.core.helpers.ThrowableToDataPointArray; public class ThrowableProxy implements java.io.Serializable { private static final long serialVersionUID = 6307784764626694851L; private ThrowableDataPoint[] tdpArray; private transient final Throwable throwable; + private transient PackageInfoCalculator packageInfoCalculator; public ThrowableProxy(Throwable throwable) { this.throwable = throwable; - tdpArray = ThrowableToDataPointArray.convert(throwable); + this.tdpArray = ThrowableToDataPointArray.convert(throwable); } public Throwable getThrowable() { return throwable; } + + public PackageInfoCalculator getPackageInfoCalculator() { + // if original instance (non-deserialized), and packageInfoCalculator + // is not already initialized, then create an instance + // here we assume that (throwable == null) for deserialized instances + if(throwable != null && packageInfoCalculator == null) { + packageInfoCalculator = new PackageInfoCalculator(); + } + return packageInfoCalculator; + } + /** * The data point representation of the throwable proxy. */ Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/spi/ThrowableToDataPointArray.java ============================================================================== --- (empty file) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/spi/ThrowableToDataPointArray.java Fri Sep 5 19:14:47 2008 @@ -0,0 +1,72 @@ +/** + * Logback: the generic, reliable, fast and flexible logging framework. + * + * Copyright (C) 2000-2008, QOS.ch + * + * This library is free software, you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation. + */ + +package ch.qos.logback.classic.spi; + +import java.util.LinkedList; +import java.util.List; + +import ch.qos.logback.core.CoreGlobal; + +/** + * Convert a throwable into an array of ThrowableDataPoint objects. + * + * + * @author Ceki Gülcü + */ +public class ThrowableToDataPointArray { + + static final ThrowableDataPoint[] TEMPLATE_ARRAY = new ThrowableDataPoint[0]; + + + static public ThrowableDataPoint[] convert(Throwable t) { + List<ThrowableDataPoint> tdpList = new LinkedList<ThrowableDataPoint>(); + extract(tdpList, t, null); + return tdpList.toArray(TEMPLATE_ARRAY); + } + + static private void extract(List<ThrowableDataPoint> tdpList, Throwable t, + StackTraceElement[] parentSTE) { + StackTraceElement[] ste = t.getStackTrace(); + final int numberOfcommonFrames = STEUtil.findNumberOfCommonFrames(ste, parentSTE); + + tdpList.add(firstLineToDataPoint(t, parentSTE)); + for (int i = 0; i < (ste.length - numberOfcommonFrames); i++) { + tdpList.add(new ThrowableDataPoint(ste[i])); + } + + if (numberOfcommonFrames != 0) { + tdpList.add(new ThrowableDataPoint("\t... "+numberOfcommonFrames + + " common frames omitted")); + } + + Throwable cause = t.getCause(); + if (cause != null) { + extract(tdpList, cause, ste); + } + } + + private static ThrowableDataPoint firstLineToDataPoint(Throwable t, + StackTraceElement[] parentSTE) { + String prefix = ""; + if (parentSTE != null) { + prefix = CoreGlobal.CAUSED_BY; + } + + String result = prefix + t.getClass().getName(); + if (t.getMessage() != null) { + result += ": " + t.getMessage(); + } + return new ThrowableDataPoint(result); + } + + + +} Modified: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/AllClassicTest.java ============================================================================== --- logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/AllClassicTest.java (original) +++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/AllClassicTest.java Fri Sep 5 19:14:47 2008 @@ -20,7 +20,6 @@ suite.addTest(ch.qos.logback.classic.PackageTest.suite()); suite.addTest(ch.qos.logback.classic.control.PackageTest.suite()); - suite.addTest(ch.qos.logback.classic.joran.PackageTest.suite()); suite.addTest(ch.qos.logback.classic.boolex.PackageTest.suite()); suite.addTest(ch.qos.logback.classic.selector.PackageTest.suite()); Modified: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/html/HTMLLayoutTest.java ============================================================================== --- logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/html/HTMLLayoutTest.java (original) +++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/html/HTMLLayoutTest.java Fri Sep 5 19:14:47 2008 @@ -20,9 +20,9 @@ import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.spi.LoggingEvent; +import ch.qos.logback.classic.spi.ThrowableDataPoint; import ch.qos.logback.classic.spi.ThrowableProxy; import ch.qos.logback.core.CoreGlobal; -import ch.qos.logback.core.helpers.ThrowableDataPoint; import ch.qos.logback.core.read.ListAppender; public class HTMLLayoutTest { Modified: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/ConverterTest.java ============================================================================== --- logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/ConverterTest.java (original) +++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/ConverterTest.java Fri Sep 5 19:14:47 2008 @@ -116,14 +116,14 @@ public void testException() { { - DynamicConverter<LoggingEvent> converter = new ThrowableInformationConverter(); + DynamicConverter<LoggingEvent> converter = new ThrowableProxyConverter(); StringBuffer buf = new StringBuffer(); converter.write(buf, le); // System.out.println(buf); } { - DynamicConverter<LoggingEvent> converter = new ThrowableInformationConverter(); + DynamicConverter<LoggingEvent> converter = new ThrowableProxyConverter(); this.optionList.add("3"); converter.setOptionList(this.optionList); StringBuffer buf = new StringBuffer(); Added: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/ExtendedThrowableProxyConverterTest.java ============================================================================== --- (empty file) +++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/ExtendedThrowableProxyConverterTest.java Fri Sep 5 19:14:47 2008 @@ -0,0 +1,71 @@ +package ch.qos.logback.classic.pattern; + +import static org.junit.Assert.assertEquals; + +import java.io.PrintWriter; +import java.io.StringWriter; + +import org.junit.After; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.spi.LoggingEvent; + +public class ExtendedThrowableProxyConverterTest { + + LoggerContext lc = new LoggerContext(); + ExtendedThrowableProxyConverter etpc = new ExtendedThrowableProxyConverter(); + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + + @Before + public void setUp() throws Exception { + etpc.setContext(lc); + etpc.start(); + } + + @After + public void tearDown() throws Exception { + } + + private LoggingEvent createLoggingEvent(Throwable t) { + LoggingEvent le = new LoggingEvent(this.getClass().getName(), lc + .getLogger(LoggerContext.ROOT_NAME), Level.DEBUG, "test message", t, + null); + return le; + } + + @Test + public void smoke() { + Exception t = new Exception("smoke"); + verify(t); + } + + @Test + @Ignore + public void nested() { + Throwable t = makeNestedException(1); + verify(t); + } + + void verify(Throwable t) { + t.printStackTrace(pw); + + LoggingEvent le = createLoggingEvent(t); + String result = etpc.convert(le); + result = result.replace("common frames omitted", "more"); + result = result.replaceAll(" \\[.*\\]", ""); + assertEquals(sw.toString(), result); + } + + Throwable makeNestedException(int level) { + if (level == 0) { + return new Exception("nesting level=" + level); + } + Throwable cause = makeNestedException(level - 1); + return new Exception("nesting level =" + level, cause); + } +} Modified: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/LRUCacheTest.java ============================================================================== --- logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/LRUCacheTest.java (original) +++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/LRUCacheTest.java Fri Sep 5 19:14:47 2008 @@ -11,6 +11,7 @@ import ch.qos.logback.classic.pattern.lru.Event; import ch.qos.logback.classic.pattern.lru.T_LRUCache; +@Ignore public class LRUCacheTest { @Test Added: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/ThrowableProxyConverterTest.java ============================================================================== --- (empty file) +++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/ThrowableProxyConverterTest.java Fri Sep 5 19:14:47 2008 @@ -0,0 +1,69 @@ +package ch.qos.logback.classic.pattern; + +import static org.junit.Assert.assertEquals; + +import java.io.PrintWriter; +import java.io.StringWriter; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.spi.LoggingEvent; + +public class ThrowableProxyConverterTest { + + LoggerContext lc = new LoggerContext(); + ThrowableProxyConverter tpc = new ThrowableProxyConverter(); + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + + @Before + public void setUp() throws Exception { + tpc.setContext(lc); + tpc.start(); + } + + @After + public void tearDown() throws Exception { + } + + private LoggingEvent createLoggingEvent(Throwable t) { + LoggingEvent le = new LoggingEvent(this.getClass().getName(), lc + .getLogger(LoggerContext.ROOT_NAME), Level.DEBUG, "test message", t, + null); + return le; + } + + @Test + public void smoke() { + Exception t = new Exception("smoke"); + verify(t); + } + + @Test + public void nested() { + Throwable t = makeNestedException(1); + verify(t); + } + + void verify(Throwable t) { + t.printStackTrace(pw); + + LoggingEvent le = createLoggingEvent(t); + String result = tpc.convert(le); + System.out.println(result); + result = result.replace("common frames omitted", "more"); + assertEquals(sw.toString(), result); + } + + Throwable makeNestedException(int level) { + if (level == 0) { + return new Exception("nesting level=" + level); + } + Throwable cause = makeNestedException(level - 1); + return new Exception("nesting level =" + level, cause); + } +} Added: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/spi/PackageInfoTest.java ============================================================================== --- (empty file) +++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/spi/PackageInfoTest.java Fri Sep 5 19:14:47 2008 @@ -0,0 +1,85 @@ +package ch.qos.logback.classic.spi; + +import java.util.Random; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import com.icegreen.greenmail.util.GreenMail; +import com.icegreen.greenmail.util.ServerSetup; + +public class PackageInfoTest { + + int diff = 1024 + new Random().nextInt(10000); + + @Before + public void setUp() throws Exception { + } + + @After + public void tearDown() throws Exception { + } + + @Test + public void withGreenMail() { + try { + ServerSetup serverSetup = new ServerSetup(-1, "localhost", + ServerSetup.PROTOCOL_SMTP); + GreenMail greenMail = new GreenMail((ServerSetup) null); + // greenMail.start(); + } catch (Throwable e) { + // e.printStackTrace(); + StackTraceElement[] stea = e.getStackTrace(); + PackageInfoCalculator pic = new PackageInfoCalculator(); + + ThrowableDataPoint[] tdpa = ThrowableToDataPointArray.convert(e); + pic.computePackageInfo(tdpa); + + + //for (ThrowableDataPoint ste : stea) { +/// + // } + } + } + + public void doPerf(boolean withPI) { + try { + ServerSetup serverSetup = new ServerSetup(-1, "localhost", + ServerSetup.PROTOCOL_SMTP); + GreenMail greenMail = new GreenMail((ServerSetup) null); + // greenMail.start(); + } catch (Throwable e) { + StackTraceElement[] stea = e.getStackTrace(); + + if (withPI) { + PackageInfoCalculator pic = new PackageInfoCalculator(); + for (StackTraceElement ste : stea) { + String className = ste.getClassName(); + //PackageInfo pi = pic.compute(className); + } + } + } + } + + double loop(int len, boolean withPI) { + long start = System.nanoTime(); + for (int i = 0; i < len; i++) { + doPerf(withPI); + } + return (1.0*System.nanoTime() - start)/len/1000; + } + + @Test + public void perfTest() { + int len = 10000; + loop(len, false); + loop(len, true); + + double d0 = loop(len, false); + System.out.println("without package info " + d0+" microseconds"); + + double d1 = loop(len, true); + System.out.println("with package info " + d1 +" microseconds"); + } +} Modified: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/spi/PackageTest.java ============================================================================== --- logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/spi/PackageTest.java (original) +++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/spi/PackageTest.java Fri Sep 5 19:14:47 2008 @@ -23,6 +23,7 @@ suite.addTestSuite(CallerDataTest.class); suite.addTest(new JUnit4TestAdapter (LoggerComparatorTest.class)); suite.addTest(new JUnit4TestAdapter (LoggingEventSerializationTest.class)); + suite.addTest(new JUnit4TestAdapter(ch.qos.logback.classic.spi.ThrowableToDataPointTest.class)); return suite; } } \ No newline at end of file Added: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/spi/PackageVersionCalculatorTest.java ============================================================================== --- (empty file) +++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/spi/PackageVersionCalculatorTest.java Fri Sep 5 19:14:47 2008 @@ -0,0 +1,47 @@ +package ch.qos.logback.classic.spi; + + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import ch.qos.logback.core.CoreGlobal; + +public class PackageVersionCalculatorTest { + + @Before + public void setUp() throws Exception { + } + + @After + public void tearDown() throws Exception { + } + + @Test + public void test() throws Exception { + Throwable t = new Throwable("x"); + //t.printStackTrace(); + ThrowableProxy tp = new ThrowableProxy(t); + PackageInfoCalculator pic = new PackageInfoCalculator(); + pic.computePackageInfo(tp.getThrowableDataPointArray()); + StringBuilder builder = new StringBuilder(); + for(ThrowableDataPoint tdp: tp.getThrowableDataPointArray()) { + String string = tdp.toString(); + builder.append(string); + extraData(builder, tdp); + builder.append(CoreGlobal.LINE_SEPARATOR); + } + System.out.println(builder.toString()); + } + + protected void extraData(StringBuilder builder, ThrowableDataPoint tdp) { + StackTraceElementProxy step = tdp.getStackTraceElementProxy(); + if(step != null) { + PackageInfo pi = step.getPackageInfo(); + if(pi != null) { + builder.append(" [").append(pi.getJarName()).append(':').append(pi.getVersion()).append(']'); + } + } + } + +} Added: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/spi/ThrowableToDataPointTest.java ============================================================================== --- (empty file) +++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/spi/ThrowableToDataPointTest.java Fri Sep 5 19:14:47 2008 @@ -0,0 +1,83 @@ +package ch.qos.logback.classic.spi; + +import static org.junit.Assert.*; + +import java.io.PrintWriter; +import java.io.StringWriter; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import ch.qos.logback.classic.spi.ThrowableDataPoint; +import ch.qos.logback.classic.spi.ThrowableToDataPointArray; +import ch.qos.logback.core.Layout; + +public class ThrowableToDataPointTest { + + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + + @Before + public void setUp() throws Exception { + } + + @After + public void tearDown() throws Exception { + } + + public void verify(Throwable t) { + t.printStackTrace(pw); + + ThrowableDataPoint[] tdpArray = ThrowableToDataPointArray.convert(t); + StringBuilder sb = new StringBuilder(); + for (ThrowableDataPoint tdp : tdpArray) { + sb.append(tdp.toString()); + sb.append(Layout.LINE_SEP); + } + String expected = sw.toString(); + String result = sb.toString().replace("common frames omitted", "more"); + + assertEquals(expected, result); + } + + @Test + public void smoke() { + Exception e = new Exception("smoke"); + verify(e); + } + + @Test + public void nested() { + Exception w = null; + try { + someMethod(); + } catch (Exception e) { + w = new Exception("wrapping", e); + } + verify(w); + } + + @Test + public void multiNested() { + Exception w = null; + try { + someOtherMethod(); + } catch (Exception e) { + w = new Exception("wrapping", e); + } + verify(w); + } + + void someMethod() throws Exception { + throw new Exception("someMethod"); + } + + void someOtherMethod() throws Exception { + try { + someMethod(); + } catch (Exception e) { + throw new Exception("someOtherMethod", e); + } + } +} Modified: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/helpers/ThrowableToStringArray.java ============================================================================== --- logback/trunk/logback-core/src/main/java/ch/qos/logback/core/helpers/ThrowableToStringArray.java (original) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/helpers/ThrowableToStringArray.java Fri Sep 5 19:14:47 2008 @@ -9,49 +9,43 @@ */ package ch.qos.logback.core.helpers; -import ch.qos.logback.core.CoreGlobal; +import java.util.LinkedList; +import java.util.List; +import ch.qos.logback.core.CoreGlobal; public class ThrowableToStringArray { - public static String[] extractStringRep(Throwable t, StackTraceElement[] parentSTE) { - String[] result; + public static String[] convert(Throwable t) { + List<String> strList = new LinkedList<String>(); + extract(strList, t, null); + return strList.toArray(new String[0]); + + } + + private static void extract(List<String> strList, Throwable t, + StackTraceElement[] parentSTE) { StackTraceElement[] ste = t.getStackTrace(); final int numberOfcommonFrames = findNumberOfCommonFrames(ste, parentSTE); - final String[] firstArray; - if (numberOfcommonFrames == 0) { - firstArray = new String[ste.length + 1]; - } else { - firstArray = new String[ste.length - numberOfcommonFrames + 2]; - } - - firstArray[0] = formatFirstLine(t, parentSTE); + strList.add(formatFirstLine(t, parentSTE)); for (int i = 0; i < (ste.length - numberOfcommonFrames); i++) { - firstArray[i + 1] = ste[i].toString(); + strList.add("\tat "+ste[i].toString()); } if (numberOfcommonFrames != 0) { - firstArray[firstArray.length - 1] = numberOfcommonFrames - + " common frames omitted"; + strList.add("\t... "+numberOfcommonFrames + " common frames omitted"); } Throwable cause = t.getCause(); if (cause != null) { - final String[] causeArray = ThrowableToStringArray.extractStringRep(cause, ste); - String[] tmp = new String[firstArray.length + causeArray.length]; - System.arraycopy(firstArray, 0, tmp, 0, firstArray.length); - System - .arraycopy(causeArray, 0, tmp, firstArray.length, causeArray.length); - result = tmp; - } else { - result = firstArray; + ThrowableToStringArray.extract(strList, cause, ste); } - return result; } - - private static String formatFirstLine(Throwable t, StackTraceElement[] parentSTE) { + + private static String formatFirstLine(Throwable t, + StackTraceElement[] parentSTE) { String prefix = ""; if (parentSTE != null) { prefix = CoreGlobal.CAUSED_BY; @@ -63,7 +57,7 @@ } return result; } - + private static int findNumberOfCommonFrames(StackTraceElement[] ste, StackTraceElement[] parentSTE) { if (parentSTE == null) { Modified: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/util/StatusPrinter.java ============================================================================== --- logback/trunk/logback-core/src/main/java/ch/qos/logback/core/util/StatusPrinter.java (original) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/util/StatusPrinter.java Fri Sep 5 19:14:47 2008 @@ -91,7 +91,7 @@ } private static void appendThrowable(StringBuilder sb, Throwable t) { - String[] stringRep = ThrowableToStringArray.extractStringRep(t, null); + String[] stringRep = ThrowableToStringArray.convert(t); for (String s : stringRep) { if (s.startsWith(CoreGlobal.CAUSED_BY)) { Modified: logback/trunk/logback-core/src/test/java/ch/qos/logback/core/AllCoreTest.java ============================================================================== --- logback/trunk/logback-core/src/test/java/ch/qos/logback/core/AllCoreTest.java (original) +++ logback/trunk/logback-core/src/test/java/ch/qos/logback/core/AllCoreTest.java Fri Sep 5 19:14:47 2008 @@ -18,6 +18,7 @@ public static Test suite() { TestSuite suite = new TestSuite(); suite.addTest(ch.qos.logback.core.util.PackageTest.suite()); + suite.addTest(ch.qos.logback.core.helpers.PackageTest.suite()); suite.addTest(ch.qos.logback.core.pattern.PackageTest.suite()); suite.addTest(ch.qos.logback.core.joran.PackageTest.suite()); suite.addTest(ch.qos.logback.core.appender.PackageTest.suite()); Added: logback/trunk/logback-core/src/test/java/ch/qos/logback/core/helpers/PackageTest.java ============================================================================== --- (empty file) +++ logback/trunk/logback-core/src/test/java/ch/qos/logback/core/helpers/PackageTest.java Fri Sep 5 19:14:47 2008 @@ -0,0 +1,15 @@ +package ch.qos.logback.core.helpers; + +import junit.framework.JUnit4TestAdapter; +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +public class PackageTest extends TestCase { + + public static Test suite() { + TestSuite suite = new TestSuite(); + suite.addTest(new JUnit4TestAdapter(ch.qos.logback.core.helpers.ThrowableToStringArrayTest.class)); + return suite; + } +} Added: logback/trunk/logback-core/src/test/java/ch/qos/logback/core/helpers/ThrowableToStringArrayTest.java ============================================================================== --- (empty file) +++ logback/trunk/logback-core/src/test/java/ch/qos/logback/core/helpers/ThrowableToStringArrayTest.java Fri Sep 5 19:14:47 2008 @@ -0,0 +1,80 @@ +package ch.qos.logback.core.helpers; + +import static org.junit.Assert.*; + +import java.io.PrintWriter; +import java.io.StringWriter; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import ch.qos.logback.core.Layout; + +public class ThrowableToStringArrayTest { + + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + + @Before + public void setUp() throws Exception { + } + + @After + public void tearDown() throws Exception { + } + + public void verify(Throwable t) { + t.printStackTrace(pw); + + String[] sa = ThrowableToStringArray.convert(t); + StringBuilder sb = new StringBuilder(); + for (String tdp : sa) { + sb.append(tdp.toString()); + sb.append(Layout.LINE_SEP); + } + String expected = sw.toString(); + String result = sb.toString().replace("common frames omitted", "more"); + assertEquals(expected, result); + } + + @Test + public void smoke() { + Exception e = new Exception("smoke"); + verify(e); + } + + @Test + public void nested() { + Exception w = null; + try { + someMethod(); + } catch (Exception e) { + w = new Exception("wrapping", e); + } + verify(w); + } + + @Test + public void multiNested() { + Exception w = null; + try { + someOtherMethod(); + } catch (Exception e) { + w = new Exception("wrapping", e); + } + verify(w); + } + + void someMethod() throws Exception { + throw new Exception("someMethod"); + } + + void someOtherMethod() throws Exception { + try { + someMethod(); + } catch (Exception e) { + throw new Exception("someOtherMethod", e); + } + } +}