Return-Path: Delivered-To: apmail-logging-log4j-dev-archive@www.apache.org Received: (qmail 6387 invoked from network); 10 Sep 2004 14:57:33 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur-2.apache.org with SMTP; 10 Sep 2004 14:57:33 -0000 Received: (qmail 80158 invoked by uid 500); 10 Sep 2004 14:57:31 -0000 Delivered-To: apmail-logging-log4j-dev-archive@logging.apache.org Received: (qmail 80024 invoked by uid 500); 10 Sep 2004 14:57:29 -0000 Mailing-List: contact log4j-dev-help@logging.apache.org; run by ezmlm Precedence: bulk List-Unsubscribe: List-Subscribe: List-Help: List-Post: List-Id: "Log4J Developers List" Reply-To: "Log4J Developers List" Delivered-To: mailing list log4j-dev@logging.apache.org Received: (qmail 80006 invoked by uid 500); 10 Sep 2004 14:57:28 -0000 Received: (qmail 79990 invoked by uid 99); 10 Sep 2004 14:57:28 -0000 X-ASF-Spam-Status: No, hits=-2.8 required=10.0 tests=ALL_TRUSTED,NO_REAL_NAME X-Spam-Check-By: apache.org Received: from [209.237.227.194] (HELO minotaur.apache.org) (209.237.227.194) by apache.org (qpsmtpd/0.28) with SMTP; Fri, 10 Sep 2004 07:57:25 -0700 Received: (qmail 6342 invoked by uid 1208); 10 Sep 2004 14:57:24 -0000 Date: 10 Sep 2004 14:57:24 -0000 Message-ID: <20040910145724.6341.qmail@minotaur.apache.org> From: ceki@apache.org To: logging-log4j-cvs@apache.org Subject: cvs commit: logging-log4j/src/java/org/apache/log4j/helpers PlatformInfo.java X-Virus-Checked: Checked X-Spam-Rating: minotaur-2.apache.org 1.6.2 0/1000/N ceki 2004/09/10 07:57:24 Modified: docs HISTORY src/java/org/apache/log4j/spi/location LocationInfo.java LegacyExtractor.java tests/src/java/org/apache/log4j/performance Loop.java src/java/org/apache/log4j/spi ThrowableInformation.java Added: src/java/org/apache/log4j/helpers PlatformInfo.java Log: - Log4j will now automatically include stack traces for nested exceptions. Log4j extends the automatic nested exception printing available in JDK 1.4 to nested exceptions accessible through methods named "getRootCause", "getNextException" and "getException", not just "getCause" as in JDK 1.4. [*] - Added o.a.l.helpers.PlatformInfo utiliity class which gives information about the current platform properties. Some classes were modified to take advantage of the PlatformInfo class. Revision Changes Path 1.112 +6 -0 logging-log4j/docs/HISTORY Index: HISTORY =================================================================== RCS file: /home/cvs/logging-log4j/docs/HISTORY,v retrieving revision 1.111 retrieving revision 1.112 diff -u -r1.111 -r1.112 --- HISTORY 7 Sep 2004 19:06:45 -0000 1.111 +++ HISTORY 10 Sep 2004 14:57:24 -0000 1.112 @@ -7,6 +7,12 @@ [D] Changes affect a method or property which was previously marked as deprecated. + + - Log4j will now automatically include stack traces for nested + exceptions. Log4j extends the automatic nested exception printing + available in JDK 1.4 to nested exceptions accessible through methods + named "getRootCause", "getNextException" and "getException", not just + "getCause" as in JDK 1.4. [*] - The location information extraction code has been refactored to use a faster mechanism when running on JDK 1.4 or later. The new mechanism, contributed 1.3 +3 -11 logging-log4j/src/java/org/apache/log4j/spi/location/LocationInfo.java Index: LocationInfo.java =================================================================== RCS file: /home/cvs/logging-log4j/src/java/org/apache/log4j/spi/location/LocationInfo.java,v retrieving revision 1.2 retrieving revision 1.3 diff -u -r1.2 -r1.3 --- LocationInfo.java 7 Sep 2004 18:58:42 -0000 1.2 +++ LocationInfo.java 10 Sep 2004 14:57:24 -0000 1.3 @@ -19,6 +19,7 @@ package org.apache.log4j.spi.location; import org.apache.log4j.helpers.LogLog; +import org.apache.log4j.helpers.PlatformInfo; /** @@ -36,8 +37,6 @@ static final long serialVersionUID = -1325822038990805636L; - private static boolean haveStackTraceElement = false; - /** * NA_LOCATION_INFO is used in conjunction with deserialized LoggingEvents * without real location info available. @@ -45,14 +44,7 @@ */ public static LocationInfo NA_LOCATION_INFO = new LocationInfo(NA, NA, NA, NA); - static { - try { - Class.forName("java.lang.StackTraceElement"); - haveStackTraceElement = true; - } catch ( Throwable e) { - // we are running on a JDK prior to 1.4 - } - } + /** Caller's line number. @@ -113,7 +105,7 @@ return; } - if(haveStackTraceElement) { + if(PlatformInfo.hasStackTraceElement()) { StackTraceElementExtractor.extract(this, t, fqnOfCallingClass); } else { LegacyExtractor.extract(this, t, fqnOfCallingClass); 1.3 +3 -14 logging-log4j/src/java/org/apache/log4j/spi/location/LegacyExtractor.java Index: LegacyExtractor.java =================================================================== RCS file: /home/cvs/logging-log4j/src/java/org/apache/log4j/spi/location/LegacyExtractor.java,v retrieving revision 1.2 retrieving revision 1.3 diff -u -r1.2 -r1.3 --- LegacyExtractor.java 7 Sep 2004 18:58:42 -0000 1.2 +++ LegacyExtractor.java 10 Sep 2004 14:57:24 -0000 1.3 @@ -18,7 +18,7 @@ package org.apache.log4j.spi.location; import org.apache.log4j.Layout; -import org.apache.log4j.helpers.LogLog; +import org.apache.log4j.helpers.PlatformInfo; import java.io.PrintWriter; import java.io.StringWriter; @@ -32,18 +32,7 @@ * @author Ceki Gülcü */ public class LegacyExtractor { - // Check if we are running in IBM's visual age. - static boolean inVisualAge = false; - static { - try { - Class dummy = Class.forName("com.ibm.uvm.tools.DebugSupport"); - inVisualAge = true; - LogLog.debug("Detected IBM VisualAge environment."); - } catch (Throwable e) { - ; // nothing to do - } - } private static StringWriter sw = new StringWriter(); private static PrintWriter pw = new PrintWriter(sw); @@ -96,7 +85,7 @@ // VA has a different stack trace format which doesn't // need to skip the inital 'at' - if (!inVisualAge) { + if (!PlatformInfo.isInVisualAge()) { // back up to first blank character ibegin = s.lastIndexOf("at ", iend); @@ -164,7 +153,7 @@ // void test.test.Run.main(java.lang.String []) int ibegin = 0; - if (inVisualAge) { + if (PlatformInfo.isInVisualAge()) { ibegin = fullInfo.lastIndexOf(' ', iend) + 1; } 1.4 +34 -17 logging-log4j/tests/src/java/org/apache/log4j/performance/Loop.java Index: Loop.java =================================================================== RCS file: /home/cvs/logging-log4j/tests/src/java/org/apache/log4j/performance/Loop.java,v retrieving revision 1.3 retrieving revision 1.4 diff -u -r1.3 -r1.4 --- Loop.java 7 Sep 2004 17:16:02 -0000 1.3 +++ Loop.java 10 Sep 2004 14:57:24 -0000 1.4 @@ -1,12 +1,12 @@ /* * Copyright 1999,2004 The Apache Software Foundation. - * + * * Licensed 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. @@ -23,19 +23,17 @@ import org.apache.log4j.PatternLayout; import org.apache.log4j.joran.JoranConfigurator; + /** * Logs in a loop a number of times and measure the elapsed time. - * + * * @author Ceki Gülcü */ public class Loop { - static int runLength; - - final static Logger logger = Logger.getLogger(Loop.class); + static final Logger logger = Logger.getLogger(Loop.class); public static void main(String[] args) throws Exception { - Logger j = Logger.getLogger("org.apache.log4j.joran"); j.setAdditivity(false); j.setLevel(Level.WARN); @@ -44,31 +42,47 @@ a.setName("console"); a.activateOptions(); j.addAppender(a); - - if (args.length == 2) + + if (args.length == 2) { init(args[0], args[1]); - else + } else { usage("Wrong number of arguments."); + } + memPrint(); loop(1000, logger, "Some fix message of medium length."); + memPrint(); + long res = loop(runLength, logger, "Some fix message of medium length."); double average = (res * 1000.0) / runLength; - System.out.println("Loop completed in [" + res + "] milliseconds, or [" - + average + "] microseconds per log."); + System.out.println( + "Loop completed in [" + res + "] milliseconds, or [" + average + + "] microseconds per log."); + memPrint(); } static void usage(String msg) { System.err.println(msg); - System.err.println("Usage: java " + Loop.class.getName() - + " runLength configFile"); + System.err.println( + "Usage: java " + Loop.class.getName() + " runLength configFile"); System.err.println("\trunLength (integer) is the length of test loop."); System.err.println("\tconfigFile is an XML configuration file"); System.exit(1); } - static void init(String runLengthStr, String configFile) throws Exception { + static void memPrint() { + Runtime runtime = Runtime.getRuntime(); + long total = runtime.totalMemory(); + long free = runtime.freeMemory(); + long used = total - free; + System.out.println( + "Total: " + total + ", free: " + free + ", used: " + used); + } + + static void init(String runLengthStr, String configFile) + throws Exception { runLength = Integer.parseInt(runLengthStr); JoranConfigurator jc = new JoranConfigurator(); jc.doConfigure(configFile, LogManager.getLoggerRepository()); @@ -78,7 +92,10 @@ long before = System.currentTimeMillis(); for (int i = 0; i < len; i++) { logger.debug(msg); + if(i == 1000) { + memPrint(); + } } return (System.currentTimeMillis() - before); } -} \ No newline at end of file +} 1.15 +87 -49 logging-log4j/src/java/org/apache/log4j/spi/ThrowableInformation.java Index: ThrowableInformation.java =================================================================== RCS file: /home/cvs/logging-log4j/src/java/org/apache/log4j/spi/ThrowableInformation.java,v retrieving revision 1.14 retrieving revision 1.15 diff -u -r1.14 -r1.15 --- ThrowableInformation.java 27 Feb 2004 16:47:34 -0000 1.14 +++ ThrowableInformation.java 10 Sep 2004 14:57:24 -0000 1.15 @@ -1,12 +1,12 @@ /* * Copyright 1999,2004 The Apache Software Foundation. - * + * * Licensed 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. @@ -19,49 +19,87 @@ import java.io.PrintWriter; import java.io.Writer; +import java.lang.reflect.Method; + import java.util.Vector; +import org.apache.log4j.helpers.PlatformInfo; + /** - * ThrowableInformation is log4j's internal representation of - * throwables. It essentially consists of a string array, called - * 'rep', where the first element, that is rep[0], represents the - * string representation of the throwable (i.e. the value you get - * when you do throwable.toString()) and subsequent elements - * correspond the stack trace with the top most entry of the stack - * corresponding to the second entry of the 'rep' array that is - * rep[1]. - * - * Note that ThrowableInformation does not store the throwable it represents. - * - * @author Ceki Gülcü - * - * */ + * ThrowableInformation is log4j's internal representation of throwables. It + * essentially consists of a string array, called 'rep', where the first + * element, that is rep[0], represents the string representation of the + * throwable (i.e. the value you get when you do throwable.toString()) and + * subsequent elements correspond the stack trace with the top most entry of the + * stack corresponding to the second entry of the 'rep' array that is rep[1]. + * + * Note that ThrowableInformation does not store the throwable it represents. + * + * @author Ceki Gülcü + * + */ public class ThrowableInformation implements java.io.Serializable { static final long serialVersionUID = -4748765566864322735L; + //private transient Throwable throwable; - private String[] rep; public ThrowableInformation(Throwable throwable) { - rep = extractStringRep(throwable); + VectorWriter vw = new VectorWriter(); + extractStringRep(throwable, vw); + rep = vw.toStringArray(); } public ThrowableInformation(String[] rep) { this.rep = rep; } - // public Throwable getThrowable() { - // return throwable; + // public Throwable getThrowable() { + // return throwable; //} - public String[] extractStringRep(Throwable throwable) { - VectorWriter vw = new VectorWriter(); - throwable.printStackTrace(vw); - String[] extractedRep = vw.toStringArray(); - return extractedRep; + public void extractStringRep(Throwable t, VectorWriter vw) { + t.printStackTrace(vw); + + // Check if the Throwable t has a nested Throwable. If so, invoke + // extractStringRep recursively. + // Note that the Throwable.getCause was added in JDK 1.4. The printStackTrace + // method was modified in JDK 1.4 to handle the nested throwable returned + // by Throwable.getCause. + try { + Class tC = t.getClass(); + Method[] mA = tC.getMethods(); + Method nextThrowableMethod = null; + for (int i = 0; i < mA.length; i++) { + if (("getCause".equals(mA[i].getName()) && !PlatformInfo.isJDK14OrLater()) + || "getRootCause".equals(mA[i].getName()) + || "getNextException".equals(mA[i].getName()) + || "getException".equals(mA[i].getName())) { + // check param types + Class[] params = mA[i].getParameterTypes(); + if ((params == null) || (params.length == 0)) { + // just found the getter for the nested throwable + nextThrowableMethod = mA[i]; + break; // no need to search further + } + } + } + + if (nextThrowableMethod != null) { + // get the nested throwable and log it + Throwable nextT = + (Throwable) nextThrowableMethod.invoke(t, new Object[0]); + if (nextT != null) { + vw.print("Root cause follows."); + extractStringRep(nextT, vw); + } + } + } catch (Exception e) { + // do nothing + } } - + /** * Retun a clone of the string representation of the exceptopn (throwable) * that this object represents. @@ -69,52 +107,52 @@ public String[] getThrowableStrRep() { return (String[]) rep.clone(); } - + public boolean equals(Object o) { - if(this == o) { + if (this == o) { return true; } - - if(!(o instanceof ThrowableInformation)) { + + if (!(o instanceof ThrowableInformation)) { return false; } - + ThrowableInformation r = (ThrowableInformation) o; - - if(rep == null) { - if(r.rep != null) { + + if (rep == null) { + if (r.rep != null) { return false; } else { return true; } } - + // at this point we know that both rep and r.rep are non-null. - if(rep.length != r.rep.length) { + if (rep.length != r.rep.length) { return false; } - + int len = rep.length; - for(int i = 0; i < len; i++) { - if(!rep[i].equals(r.rep[i])) { + for (int i = 0; i < len; i++) { + if (!rep[i].equals(r.rep[i])) { return false; } } - + return true; } } /** - * VectorWriter is a seemingly trivial implemtantion of PrintWriter. - * The throwable instance that we are trying to represnt is asked to - * print itself to a VectorWriter. - * - * By our design choice, r string representation of the throwable - * does not contain any line separators. It follows that println() - * methods of VectorWriter ignore the 'ln' part. - * */ + * VectorWriter is a seemingly trivial implemtantion of PrintWriter. The + * throwable instance that we are trying to represnt is asked to print itself to + * a VectorWriter. + * + * By our design choice, r string representation of the throwable does not + * contain any line separators. It follows that println() methods of + * VectorWriter ignore the 'ln' part. + */ class VectorWriter extends PrintWriter { private Vector v; 1.1 logging-log4j/src/java/org/apache/log4j/helpers/PlatformInfo.java Index: PlatformInfo.java =================================================================== /* * Copyright 1999,2004 The Apache Software Foundation. * * Licensed 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.log4j.helpers; import java.util.Properties; /** * This class provides information about the runtime platform. * * @author Ceki Gulcu * @since 1.3 * */ public class PlatformInfo { private static final int UNINITIALIZED = -1; // Check if we are running in IBM's visual age. private static int inVisualAge = UNINITIALIZED; private static int onAS400 = UNINITIALIZED; private static int hasStackTraceElement = UNINITIALIZED; public static boolean isInVisualAge() { if (inVisualAge == UNINITIALIZED) { try { Class dummy = Class.forName("com.ibm.uvm.tools.DebugSupport"); inVisualAge = 1; } catch (Throwable e) { inVisualAge = 0; } } return (inVisualAge == 1); } /** * Are we running on AS400? */ public static boolean isOnAS400() { if (onAS400 == UNINITIALIZED) { try { Properties p = System.getProperties(); String osname = p.getProperty("os.name"); if ((osname != null) && (osname.equals("OS/400"))) { onAS400 = 1; } else { onAS400 = 0; } } catch (Throwable e) { // This should not happen, but if it does, assume we are not on // AS400. onAS400 = 0; } } return (onAS400 == 1); } public static boolean hasStackTraceElement() { if (hasStackTraceElement == UNINITIALIZED) { try { Class.forName("java.lang.StackTraceElement"); hasStackTraceElement = 1; } catch (Throwable e) { // we are running on a JDK prior to 1.4 hasStackTraceElement = 0; } } return (hasStackTraceElement == 1); } public static boolean isJDK14OrLater() { return hasStackTraceElement(); } } --------------------------------------------------------------------- To unsubscribe, e-mail: log4j-dev-unsubscribe@logging.apache.org For additional commands, e-mail: log4j-dev-help@logging.apache.org