You are viewing a plain text version of this content. The canonical link for it is here.
Posted to log4j-dev@logging.apache.org by ca...@apache.org on 2008/09/20 04:37:56 UTC

svn commit: r697315 - in /logging/log4j/trunk: src/changes/ src/main/java/org/apache/log4j/ src/main/java/org/apache/log4j/spi/ src/main/java/org/apache/log4j/xml/ src/main/resources/org/apache/log4j/xml/ tests/input/xml/ tests/src/java/org/apache/log4...

Author: carnold
Date: Fri Sep 19 19:37:56 2008
New Revision: 697315

URL: http://svn.apache.org/viewvc?rev=697315&view=rev
Log:
Bug 45721: Add configurable ThrowableRenderers

Added:
    logging/log4j/trunk/src/main/java/org/apache/log4j/DefaultThrowableRenderer.java
    logging/log4j/trunk/src/main/java/org/apache/log4j/EnhancedThrowableRenderer.java
    logging/log4j/trunk/src/main/java/org/apache/log4j/spi/ThrowableRenderer.java
    logging/log4j/trunk/src/main/java/org/apache/log4j/spi/ThrowableRendererSupport.java
    logging/log4j/trunk/tests/input/xml/throwableRenderer1.xml
    logging/log4j/trunk/tests/src/java/org/apache/log4j/DefaultThrowableRendererTest.java
    logging/log4j/trunk/tests/src/java/org/apache/log4j/EnhancedThrowableRendererTest.java
Modified:
    logging/log4j/trunk/src/changes/changes.xml
    logging/log4j/trunk/src/main/java/org/apache/log4j/Hierarchy.java
    logging/log4j/trunk/src/main/java/org/apache/log4j/PropertyConfigurator.java
    logging/log4j/trunk/src/main/java/org/apache/log4j/spi/LoggingEvent.java
    logging/log4j/trunk/src/main/java/org/apache/log4j/spi/ThrowableInformation.java
    logging/log4j/trunk/src/main/java/org/apache/log4j/xml/DOMConfigurator.java
    logging/log4j/trunk/src/main/resources/org/apache/log4j/xml/log4j.dtd
    logging/log4j/trunk/tests/src/java/org/apache/log4j/CoreTestSuite.java
    logging/log4j/trunk/tests/src/java/org/apache/log4j/PatternLayoutTestCase.java
    logging/log4j/trunk/tests/src/java/org/apache/log4j/PropertyConfiguratorTest.java
    logging/log4j/trunk/tests/src/java/org/apache/log4j/xml/DOMTestCase.java

Modified: logging/log4j/trunk/src/changes/changes.xml
URL: http://svn.apache.org/viewvc/logging/log4j/trunk/src/changes/changes.xml?rev=697315&r1=697314&r2=697315&view=diff
==============================================================================
--- logging/log4j/trunk/src/changes/changes.xml (original)
+++ logging/log4j/trunk/src/changes/changes.xml Fri Sep 19 19:37:56 2008
@@ -61,6 +61,7 @@
        <action action="fix" issue="45636">2 tests for DateLayout are failing because of ill initialized DateFormat.</action>
        <action action="fix" issue="45659">Incorrect user mailing list URL.</action>
        <action action="fix" issue="44386">NTEventLogAppender.dll for 64-bit editions for Microsoft Windows.</action>
+       <action action="fix" issue="45721">Add configuration of ThrowableRenderers and add org.apache.log4j.EnhancedThrowableRenderer.</action>
     </release>
 
   

Added: logging/log4j/trunk/src/main/java/org/apache/log4j/DefaultThrowableRenderer.java
URL: http://svn.apache.org/viewvc/logging/log4j/trunk/src/main/java/org/apache/log4j/DefaultThrowableRenderer.java?rev=697315&view=auto
==============================================================================
--- logging/log4j/trunk/src/main/java/org/apache/log4j/DefaultThrowableRenderer.java (added)
+++ logging/log4j/trunk/src/main/java/org/apache/log4j/DefaultThrowableRenderer.java Fri Sep 19 19:37:56 2008
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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;
+
+import org.apache.log4j.spi.ThrowableRenderer;
+
+import java.io.StringWriter;
+import java.io.PrintWriter;
+import java.io.LineNumberReader;
+import java.io.StringReader;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.util.ArrayList;
+
+/**
+ * Default implementation of ThrowableRenderer using
+ * Throwable.printStackTrace.
+ *
+ * @since 1.2.16
+ */
+public final class DefaultThrowableRenderer implements ThrowableRenderer {
+    /**
+     * Construct new instance.
+     */
+    public DefaultThrowableRenderer() {
+
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public String[] doRender(final Throwable throwable) {
+        return render(throwable);
+    }
+
+    /**
+     * Render throwable using Throwable.printStackTrace.
+     * @param throwable throwable, may not be null.
+     * @return string representation.
+     */
+    public static String[] render(final Throwable throwable) {
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+        throwable.printStackTrace(pw);
+        pw.flush();
+        LineNumberReader reader = new LineNumberReader(
+                new StringReader(sw.toString()));
+        ArrayList lines = new ArrayList();
+        try {
+          String line = reader.readLine();
+          while(line != null) {
+            lines.add(line);
+            line = reader.readLine();
+          }
+        } catch(IOException ex) {
+            if (ex instanceof InterruptedIOException) {
+                Thread.currentThread().interrupt();
+            }
+            lines.add(ex.toString());
+        }
+        String[] tempRep = new String[lines.size()];
+        lines.toArray(tempRep);
+        return tempRep;
+    }
+}

Added: logging/log4j/trunk/src/main/java/org/apache/log4j/EnhancedThrowableRenderer.java
URL: http://svn.apache.org/viewvc/logging/log4j/trunk/src/main/java/org/apache/log4j/EnhancedThrowableRenderer.java?rev=697315&view=auto
==============================================================================
--- logging/log4j/trunk/src/main/java/org/apache/log4j/EnhancedThrowableRenderer.java (added)
+++ logging/log4j/trunk/src/main/java/org/apache/log4j/EnhancedThrowableRenderer.java Fri Sep 19 19:37:56 2008
@@ -0,0 +1,157 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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;
+
+import org.apache.log4j.spi.ThrowableRenderer;
+
+import java.io.File;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.security.CodeSource;
+
+/**
+ * Enhanced implementation of ThrowableRenderer.  Uses Throwable.getStackTrace
+ * if running on JDK 1.4 or later and delegates to DefaultThrowableRenderer.render
+ * on earlier virtual machines.
+ *
+ * @since 1.2.16
+ */
+public final class EnhancedThrowableRenderer implements ThrowableRenderer {
+    /**
+     * Throwable.getStackTrace() method.
+     */
+    private Method getStackTraceMethod;
+    /**
+     * StackTraceElement.getClassName() method.
+     */
+    private Method getClassNameMethod;
+
+
+    /**
+     * Construct new instance.
+     */
+    public EnhancedThrowableRenderer() {
+        try {
+            Class[] noArgs = null;
+            getStackTraceMethod = Throwable.class.getMethod("getStackTrace", noArgs);
+            Class ste = Class.forName("java.lang.StackTraceElement");
+            getClassNameMethod = ste.getMethod("getClassName", noArgs);
+        } catch(Exception ex) {
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String[] doRender(final Throwable throwable) {
+        if (getStackTraceMethod != null) {
+            try {
+                Object[] noArgs = null;
+                Object[] elements = (Object[]) getStackTraceMethod.invoke(throwable, noArgs);
+                String[] lines = new String[elements.length + 1];
+                lines[0] = throwable.toString();
+                for(int i = 0; i < elements.length; i++) {
+                    lines[i+1] = formatElement(elements[i]);
+                }
+                return lines;
+            } catch(Exception ex) {
+            }
+        }
+        return DefaultThrowableRenderer.render(throwable);
+    }
+
+    /**
+     * Format one element from stack trace.
+     * @param element element, may not be null.
+     * @return string representation of element.
+     */
+    private String formatElement(final Object element) {
+        StringBuffer buf = new StringBuffer("\tat ");
+        buf.append(element);
+        try {
+            String className = getClassNameMethod.invoke(element, (Object[]) null).toString();
+            Class cls = findClass(className);
+            buf.append('[');
+            try {
+                CodeSource source = cls.getProtectionDomain().getCodeSource();
+                if (source != null) {
+                   URL locationURL = source.getLocation();
+                   if (locationURL != null) {
+                       //
+                       //   if a file: URL
+                       //
+                       if ("file".equals(locationURL.getProtocol())) {
+                           String path = locationURL.getPath();
+                           if (path != null) {
+                               //
+                               //  find the last file separator character
+                               //
+                               int lastSlash = path.lastIndexOf('/');
+                               int lastBack = path.lastIndexOf(File.separatorChar);
+                               if (lastBack > lastSlash) {
+                                   lastSlash = lastBack;
+                               }
+                               //
+                               //  if no separator or ends with separator (a directory)
+                               //     then output the URL, otherwise just the file name.
+                               //
+                               if (lastSlash <= 0 || lastSlash == path.length() - 1) {
+                                   buf.append(locationURL);
+                               } else {
+                                   buf.append(path.substring(lastSlash + 1));
+                               }
+                           }
+                       } else {
+                           buf.append(locationURL);
+                       }
+                   }
+                }
+            } catch(SecurityException ex) {
+            }
+            buf.append(':');
+            Package pkg = cls.getPackage();
+            if (pkg != null) {
+                String implVersion = pkg.getImplementationVersion();
+                if (implVersion != null) {
+                    buf.append(implVersion);
+                }
+            }
+            buf.append(']');
+        } catch(Exception ex) {
+        }
+        return buf.toString();
+    }
+
+    /**
+     * Find class given class name.
+     * @param className class name, may not be null.
+     * @return class, will not be null.
+     * @throws ClassNotFoundException thrown if class can not be found.
+     */
+    private Class findClass(final String className) throws ClassNotFoundException {
+     try {
+       return Thread.currentThread().getContextClassLoader().loadClass(className);
+     } catch (ClassNotFoundException e) {
+       try {
+         return Class.forName(className);
+       } catch (ClassNotFoundException e1) {
+          return getClass().getClassLoader().loadClass(className);
+      }
+    }
+  }
+
+}

Modified: logging/log4j/trunk/src/main/java/org/apache/log4j/Hierarchy.java
URL: http://svn.apache.org/viewvc/logging/log4j/trunk/src/main/java/org/apache/log4j/Hierarchy.java?rev=697315&r1=697314&r2=697315&view=diff
==============================================================================
--- logging/log4j/trunk/src/main/java/org/apache/log4j/Hierarchy.java (original)
+++ logging/log4j/trunk/src/main/java/org/apache/log4j/Hierarchy.java Fri Sep 19 19:37:56 2008
@@ -36,10 +36,11 @@
 import org.apache.log4j.spi.HierarchyEventListener;
 import org.apache.log4j.spi.LoggerRepository;
 import org.apache.log4j.spi.RendererSupport;
-import org.apache.log4j.Appender;
 import org.apache.log4j.or.RendererMap;
 import org.apache.log4j.or.ObjectRenderer;
 import org.apache.log4j.helpers.LogLog;
+import org.apache.log4j.spi.ThrowableRendererSupport;
+import org.apache.log4j.spi.ThrowableRenderer;
 
 /**
    This class is specialized in retrieving loggers by name and also
@@ -62,7 +63,7 @@
    @author Ceki G&uuml;lc&uuml;
 
 */
-public class Hierarchy implements LoggerRepository, RendererSupport {
+public class Hierarchy implements LoggerRepository, RendererSupport, ThrowableRendererSupport {
 
   private LoggerFactory defaultFactory;
   private Vector listeners;
@@ -77,6 +78,8 @@
   boolean emittedNoAppenderWarning = false;
   boolean emittedNoResourceBundleWarning = false;
 
+  private ThrowableRenderer throwableRenderer = null;
+
   /**
      Create a new logger hierarchy.
 
@@ -396,10 +399,11 @@
       }
     }
     rendererMap.clear();
+    throwableRenderer = null;
   }
 
   /**
-     Does mothing.
+     Does nothing.
 
      @deprecated Deprecated with no replacement.
    */
@@ -418,6 +422,20 @@
     rendererMap.put(renderedClass, renderer);
   }
 
+    /**
+     * {@inheritDoc}
+     */
+  public void setThrowableRenderer(final ThrowableRenderer renderer) {
+      throwableRenderer = renderer;
+  }
+
+    /**
+     * {@inheritDoc}
+     */
+  public ThrowableRenderer getThrowableRenderer() {
+      return throwableRenderer;
+  }
+
 
   /**
      Shutting down a hierarchy will <em>safely</em> close and remove

Modified: logging/log4j/trunk/src/main/java/org/apache/log4j/PropertyConfigurator.java
URL: http://svn.apache.org/viewvc/logging/log4j/trunk/src/main/java/org/apache/log4j/PropertyConfigurator.java?rev=697315&r1=697314&r2=697315&view=diff
==============================================================================
--- logging/log4j/trunk/src/main/java/org/apache/log4j/PropertyConfigurator.java (original)
+++ logging/log4j/trunk/src/main/java/org/apache/log4j/PropertyConfigurator.java Fri Sep 19 19:37:56 2008
@@ -32,6 +32,8 @@
 import org.apache.log4j.spi.LoggerRepository;
 import org.apache.log4j.spi.OptionHandler;
 import org.apache.log4j.spi.RendererSupport;
+import org.apache.log4j.spi.ThrowableRenderer;
+import org.apache.log4j.spi.ThrowableRendererSupport;
 
 import java.io.FileInputStream;
 import java.io.InputStream;
@@ -100,6 +102,7 @@
   static final String      APPENDER_PREFIX = "log4j.appender.";
   static final String      RENDERER_PREFIX = "log4j.renderer.";
   static final String      THRESHOLD_PREFIX = "log4j.threshold";
+  private static final String      THROWABLE_RENDERER_PREFIX = "log4j.throwableRenderer";
 
   /** Key for specifying the {@link org.apache.log4j.spi.LoggerFactory
       LoggerFactory}.  Currently set to "<code>log4j.loggerFactory</code>".  */
@@ -232,6 +235,24 @@
     log4j.renderer.my.Fruit=my.FruitRenderer
     </pre>
 
+   <h3>ThrowableRenderer</h3>
+
+   You can customize the way an instance of Throwable is
+   converted to String before being logged. This is done by
+   specifying an {@link org.apache.log4j.spi.ThrowableRenderer ThrowableRenderer}.
+
+   <p>The syntax is:
+
+   <pre>
+   log4j.throwableRenderer=fully.qualified.name.of.rendering.class
+   log4j.throwableRenderer.paramName=paramValue
+   </pre>
+
+   As in,
+   <pre>
+   log4j.throwableRenderer=org.apache.log4j.EnhancedThrowableRenderer
+   </pre>
+
     <h3>Logger Factories</h3>
 
     The usage of custom logger factories is discouraged and no longer
@@ -588,6 +609,23 @@
 	  RendererMap.addRenderer((RendererSupport) hierarchy, renderedClass,
 				  renderingClass);
 	}
+      } else if (key.equals(THROWABLE_RENDERER_PREFIX)) {
+          if (hierarchy instanceof ThrowableRendererSupport) {
+            ThrowableRenderer tr = (ThrowableRenderer)
+                  OptionConverter.instantiateByKey(props,
+                          THROWABLE_RENDERER_PREFIX,
+                          org.apache.log4j.spi.ThrowableRenderer.class,
+                          null);
+            if(tr == null) {
+                LogLog.error(
+                    "Could not instantiate throwableRenderer.");
+            } else {
+                PropertySetter setter = new PropertySetter(tr);
+                setter.setProperties(props, THROWABLE_RENDERER_PREFIX + ".");
+                ((ThrowableRendererSupport) hierarchy).setThrowableRenderer(tr);
+
+            }
+          }
       }
     }
   }

Modified: logging/log4j/trunk/src/main/java/org/apache/log4j/spi/LoggingEvent.java
URL: http://svn.apache.org/viewvc/logging/log4j/trunk/src/main/java/org/apache/log4j/spi/LoggingEvent.java?rev=697315&r1=697314&r2=697315&view=diff
==============================================================================
--- logging/log4j/trunk/src/main/java/org/apache/log4j/spi/LoggingEvent.java (original)
+++ logging/log4j/trunk/src/main/java/org/apache/log4j/spi/LoggingEvent.java Fri Sep 19 19:37:56 2008
@@ -162,7 +162,7 @@
     this.level = level;
     this.message = message;
     if(throwable != null) {
-      this.throwableInfo = new ThrowableInformation(throwable);
+      this.throwableInfo = new ThrowableInformation(throwable, logger);
     }
     timeStamp = System.currentTimeMillis();
   }
@@ -187,7 +187,7 @@
     this.level = level;
     this.message = message;
     if(throwable != null) {
-      this.throwableInfo = new ThrowableInformation(throwable);
+      this.throwableInfo = new ThrowableInformation(throwable, logger);
     }
 
     this.timeStamp = timeStamp;

Modified: logging/log4j/trunk/src/main/java/org/apache/log4j/spi/ThrowableInformation.java
URL: http://svn.apache.org/viewvc/logging/log4j/trunk/src/main/java/org/apache/log4j/spi/ThrowableInformation.java?rev=697315&r1=697314&r2=697315&view=diff
==============================================================================
--- logging/log4j/trunk/src/main/java/org/apache/log4j/spi/ThrowableInformation.java (original)
+++ logging/log4j/trunk/src/main/java/org/apache/log4j/spi/ThrowableInformation.java Fri Sep 19 19:37:56 2008
@@ -17,8 +17,8 @@
 
 package org.apache.log4j.spi;
 
-import java.io.*;
-import java.util.ArrayList;
+import org.apache.log4j.Category;
+import org.apache.log4j.DefaultThrowableRenderer;
 
 /**
   * ThrowableInformation is log4j's internal representation of
@@ -38,6 +38,7 @@
   static final long serialVersionUID = -4748765566864322735L;
 
   private transient Throwable throwable;
+  private transient Category category;
   private String[] rep;
 
   public
@@ -46,6 +47,17 @@
   }
 
     /**
+     * Create a new instance.
+     * @param throwable throwable, may not be null.
+     * @param category category used to obtain ThrowableRenderer, may be null.
+     * @since 1.2.16
+     */
+  public ThrowableInformation(Throwable throwable, Category category) {
+      this.throwable = throwable;
+      this.category = category;
+  }
+
+    /**
      * Create new instance.
      * @since 1.2.15
      * @param r String representation of throwable.
@@ -62,31 +74,20 @@
     return throwable;
   }
 
-  public
-  String[] getThrowableStrRep() {
+  public synchronized String[] getThrowableStrRep() {
     if(rep == null) {
-      StringWriter sw = new StringWriter();
-      PrintWriter pw = new PrintWriter(sw);
-      throwable.printStackTrace(pw);
-      pw.flush();
-      LineNumberReader reader = new LineNumberReader(
-              new StringReader(sw.toString()));
-      ArrayList lines = new ArrayList();
-      try {
-        String line = reader.readLine();
-        while(line != null) {
-          lines.add(line);
-          line = reader.readLine();
-        }
-      } catch(IOException ex) {
-          if (ex instanceof InterruptedIOException) {
-              Thread.currentThread().interrupt();
+      ThrowableRenderer renderer = null;
+      if (category != null) {
+          LoggerRepository repo = category.getLoggerRepository();
+          if (repo instanceof ThrowableRendererSupport) {
+              renderer = ((ThrowableRendererSupport) repo).getThrowableRenderer();
           }
-          lines.add(ex.toString());
       }
-      String[] tempRep = new String[lines.size()];
-      lines.toArray(tempRep);
-      rep = tempRep;
+      if (renderer == null) {
+          rep = DefaultThrowableRenderer.render(throwable);
+      } else {
+          rep = renderer.doRender(throwable);
+      }
     }
     return (String[]) rep.clone();
   }

Added: logging/log4j/trunk/src/main/java/org/apache/log4j/spi/ThrowableRenderer.java
URL: http://svn.apache.org/viewvc/logging/log4j/trunk/src/main/java/org/apache/log4j/spi/ThrowableRenderer.java?rev=697315&view=auto
==============================================================================
--- logging/log4j/trunk/src/main/java/org/apache/log4j/spi/ThrowableRenderer.java (added)
+++ logging/log4j/trunk/src/main/java/org/apache/log4j/spi/ThrowableRenderer.java Fri Sep 19 19:37:56 2008
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.spi;
+
+/**
+ * Implemented by classes that render instances of
+ * java.lang.Throwable (exceptions and errors)
+ * into a string representation.
+ *
+ * @since 1.2.16
+ */
+public interface ThrowableRenderer {
+    /**
+     * Render Throwable.
+     * @param t throwable, may not be null.
+     * @return String representation.
+     */
+    public String[] doRender(Throwable t);
+}

Added: logging/log4j/trunk/src/main/java/org/apache/log4j/spi/ThrowableRendererSupport.java
URL: http://svn.apache.org/viewvc/logging/log4j/trunk/src/main/java/org/apache/log4j/spi/ThrowableRendererSupport.java?rev=697315&view=auto
==============================================================================
--- logging/log4j/trunk/src/main/java/org/apache/log4j/spi/ThrowableRendererSupport.java (added)
+++ logging/log4j/trunk/src/main/java/org/apache/log4j/spi/ThrowableRendererSupport.java Fri Sep 19 19:37:56 2008
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.spi;
+
+/**
+ * Implemented by logger repositories that support configurable
+ * rendering of Throwables.
+ *
+ * @since 1.2.16
+ */
+public interface ThrowableRendererSupport {
+    /**
+     * Get throwable renderer.
+     * @return throwable renderer, may be null.
+     */
+    ThrowableRenderer getThrowableRenderer();
+
+    /**
+     * Set throwable renderer.
+     * @param renderer renderer, may be null.
+     */
+    void setThrowableRenderer(ThrowableRenderer renderer);
+}

Modified: logging/log4j/trunk/src/main/java/org/apache/log4j/xml/DOMConfigurator.java
URL: http://svn.apache.org/viewvc/logging/log4j/trunk/src/main/java/org/apache/log4j/xml/DOMConfigurator.java?rev=697315&r1=697314&r2=697315&view=diff
==============================================================================
--- logging/log4j/trunk/src/main/java/org/apache/log4j/xml/DOMConfigurator.java (original)
+++ logging/log4j/trunk/src/main/java/org/apache/log4j/xml/DOMConfigurator.java Fri Sep 19 19:37:56 2008
@@ -35,6 +35,8 @@
 import org.apache.log4j.spi.LoggerFactory;
 import org.apache.log4j.spi.LoggerRepository;
 import org.apache.log4j.spi.RendererSupport;
+import org.apache.log4j.spi.ThrowableRenderer;
+import org.apache.log4j.spi.ThrowableRendererSupport;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.w3c.dom.NamedNodeMap;
@@ -89,6 +91,7 @@
   static final String CONFIGURATION_TAG = "log4j:configuration";
   static final String OLD_CONFIGURATION_TAG = "configuration";
   static final String RENDERER_TAG      = "renderer";
+  private static final String THROWABLE_RENDERER_TAG = "throwableRenderer";
   static final String APPENDER_TAG 	= "appender";
   static final String APPENDER_REF_TAG 	= "appender-ref";  
   static final String PARAM_TAG    	= "param";
@@ -591,6 +594,49 @@
     }
   }
 
+    /**
+     * Parses throwable renderer.
+     * @param element throwableRenderer element.
+     * @return configured throwable renderer.
+     * @since 1.2.16.
+     */
+    protected ThrowableRenderer parseThrowableRenderer(final Element element) {
+        String className = subst(element.getAttribute(CLASS_ATTR));
+        LogLog.debug("Parsing throwableRenderer of class: \""+className+"\"");
+        try {
+          Object instance 	= Loader.loadClass(className).newInstance();
+          ThrowableRenderer tr   	= (ThrowableRenderer)instance;
+          PropertySetter propSetter = new PropertySetter(tr);
+
+          NodeList params 	= element.getChildNodes();
+          final int length 	= params.getLength();
+
+          for (int loop = 0; loop < length; loop++) {
+                Node currentNode = (Node)params.item(loop);
+                if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
+                    Element currentElement = (Element) currentNode;
+                    String tagName = currentElement.getTagName();
+                    if(tagName.equals(PARAM_TAG)) {
+                        setParameter(currentElement, propSetter);
+                    } else {
+                        parseUnrecognizedElement(instance, currentElement, props);
+                    }
+                }
+          }
+
+          propSetter.activate();
+          return tr;
+        }
+        catch (Exception oops) {
+            if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) {
+                Thread.currentThread().interrupt();
+            }
+            LogLog.error("Could not create the ThrowableRenderer. Reported error follows.",
+               oops);
+          return null;
+        }
+    }
+
   /**
      Used internally to parse a level  element.
   */
@@ -950,7 +996,14 @@
 	  parseRoot(currentElement);
 	} else if(tagName.equals(RENDERER_TAG)) {
 	  parseRenderer(currentElement);
-	} else if (!(tagName.equals(APPENDER_TAG)
+    } else if(tagName.equals(THROWABLE_RENDERER_TAG)) {
+        if (repository instanceof ThrowableRendererSupport) {
+            ThrowableRenderer tr = parseThrowableRenderer(currentElement);
+            if (tr != null) {
+                ((ThrowableRendererSupport) repository).setThrowableRenderer(tr);
+            }
+        }
+    } else if (!(tagName.equals(APPENDER_TAG)
             || tagName.equals(CATEGORY_FACTORY_TAG)
             || tagName.equals(LOGGER_FACTORY_TAG))) {
         quietParseUnrecognizedElement(repository, currentElement, props);

Modified: logging/log4j/trunk/src/main/resources/org/apache/log4j/xml/log4j.dtd
URL: http://svn.apache.org/viewvc/logging/log4j/trunk/src/main/resources/org/apache/log4j/xml/log4j.dtd?rev=697315&r1=697314&r2=697315&view=diff
==============================================================================
--- logging/log4j/trunk/src/main/resources/org/apache/log4j/xml/log4j.dtd (original)
+++ logging/log4j/trunk/src/main/resources/org/apache/log4j/xml/log4j.dtd Fri Sep 19 19:37:56 2008
@@ -24,7 +24,8 @@
 elements,appender elements, categories and an optional root
 element. -->
 
-<!ELEMENT log4j:configuration (renderer*, appender*,plugin*, (category|logger)*,root?,
+<!ELEMENT log4j:configuration (renderer*, throwableRenderer?,
+                               appender*,plugin*, (category|logger)*,root?,
                                (categoryFactory|loggerFactory)?)>
 
 <!-- The "threshold" attribute takes a level value below which -->
@@ -56,6 +57,14 @@
   renderingClass CDATA #REQUIRED
 >
 
+<!--  throwableRenderer allows the user to customize the conversion
+         of exceptions to a string representation.  -->
+<!ELEMENT throwableRenderer (param*)>
+<!ATTLIST throwableRenderer
+  class  CDATA #REQUIRED
+>
+
+
 <!-- Appenders must have a name and a class. -->
 <!-- Appenders may contain an error handler, a layout, optional parameters -->
 <!-- and filters. They may also reference (or include) other appenders. -->

Added: logging/log4j/trunk/tests/input/xml/throwableRenderer1.xml
URL: http://svn.apache.org/viewvc/logging/log4j/trunk/tests/input/xml/throwableRenderer1.xml?rev=697315&view=auto
==============================================================================
--- logging/log4j/trunk/tests/input/xml/throwableRenderer1.xml (added)
+++ logging/log4j/trunk/tests/input/xml/throwableRenderer1.xml Fri Sep 19 19:37:56 2008
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+-->
+
+<log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/'>
+
+    <throwableRenderer class="org.apache.log4j.xml.DOMTestCase$MockThrowableRenderer">
+        <param name="showVersion" value="false"/>
+    </throwableRenderer>
+</log4j:configuration>

Modified: logging/log4j/trunk/tests/src/java/org/apache/log4j/CoreTestSuite.java
URL: http://svn.apache.org/viewvc/logging/log4j/trunk/tests/src/java/org/apache/log4j/CoreTestSuite.java?rev=697315&r1=697314&r2=697315&view=diff
==============================================================================
--- logging/log4j/trunk/tests/src/java/org/apache/log4j/CoreTestSuite.java (original)
+++ logging/log4j/trunk/tests/src/java/org/apache/log4j/CoreTestSuite.java Fri Sep 19 19:37:56 2008
@@ -52,6 +52,8 @@
         s.addTestSuite(org.apache.log4j.PropertyConfiguratorTest.class);
         s.addTestSuite(org.apache.log4j.net.SMTPAppenderTest.class);
         s.addTestSuite(org.apache.log4j.net.TelnetAppenderTest.class);
+        s.addTestSuite(org.apache.log4j.DefaultThrowableRendererTest.class);
+        s.addTestSuite(org.apache.log4j.EnhancedThrowableRendererTest.class);
         return s;
     }
 }

Added: logging/log4j/trunk/tests/src/java/org/apache/log4j/DefaultThrowableRendererTest.java
URL: http://svn.apache.org/viewvc/logging/log4j/trunk/tests/src/java/org/apache/log4j/DefaultThrowableRendererTest.java?rev=697315&view=auto
==============================================================================
--- logging/log4j/trunk/tests/src/java/org/apache/log4j/DefaultThrowableRendererTest.java (added)
+++ logging/log4j/trunk/tests/src/java/org/apache/log4j/DefaultThrowableRendererTest.java Fri Sep 19 19:37:56 2008
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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;
+
+import junit.framework.TestCase;
+import org.apache.log4j.spi.ThrowableRenderer;
+
+public class DefaultThrowableRendererTest extends TestCase {
+    public DefaultThrowableRendererTest(final String name) {
+        super(name);
+    }
+
+    public void testDefaultRender() {
+        ThrowableRenderer r = new DefaultThrowableRenderer();
+        Exception ex = new Exception();
+        String[] strRep = r.doRender(ex);
+        assertNotNull(strRep);
+        assertTrue(strRep.length > 0);
+        for(int i = 0; i < strRep.length; i++) {
+            assertNotNull(strRep[i]);
+        }
+    }
+}

Added: logging/log4j/trunk/tests/src/java/org/apache/log4j/EnhancedThrowableRendererTest.java
URL: http://svn.apache.org/viewvc/logging/log4j/trunk/tests/src/java/org/apache/log4j/EnhancedThrowableRendererTest.java?rev=697315&view=auto
==============================================================================
--- logging/log4j/trunk/tests/src/java/org/apache/log4j/EnhancedThrowableRendererTest.java (added)
+++ logging/log4j/trunk/tests/src/java/org/apache/log4j/EnhancedThrowableRendererTest.java Fri Sep 19 19:37:56 2008
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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;
+
+import junit.framework.TestCase;
+import org.apache.log4j.spi.ThrowableRenderer;
+
+/**
+ * Test for EnhancedThrowableRenderer.
+ *
+ */
+public class EnhancedThrowableRendererTest extends TestCase {
+    /**
+     * Construct new instance.
+     * @param name test name.
+     */
+    public EnhancedThrowableRendererTest(final String name) {
+        super(name);
+    }
+
+    /**
+     * Render simple exception.
+     */
+    public void testEnhancedRender() {
+        ThrowableRenderer r = new EnhancedThrowableRenderer();
+        Exception ex = new Exception();
+        String[] strRep = r.doRender(ex);
+        assertNotNull(strRep);
+        assertTrue(strRep.length > 0);
+        for(int i = 0; i < strRep.length; i++) {
+            assertNotNull(strRep[i]);
+        }
+    }
+}

Modified: logging/log4j/trunk/tests/src/java/org/apache/log4j/PatternLayoutTestCase.java
URL: http://svn.apache.org/viewvc/logging/log4j/trunk/tests/src/java/org/apache/log4j/PatternLayoutTestCase.java?rev=697315&r1=697314&r2=697315&view=diff
==============================================================================
--- logging/log4j/trunk/tests/src/java/org/apache/log4j/PatternLayoutTestCase.java (original)
+++ logging/log4j/trunk/tests/src/java/org/apache/log4j/PatternLayoutTestCase.java Fri Sep 19 19:37:56 2008
@@ -285,6 +285,7 @@
     assertTrue(Compare.compare(FILTERED, "witness/patternLayout.14"));
   }
 
+
   void common() {
     String oldThreadName = Thread.currentThread().getName();
     Thread.currentThread().setName("main");

Modified: logging/log4j/trunk/tests/src/java/org/apache/log4j/PropertyConfiguratorTest.java
URL: http://svn.apache.org/viewvc/logging/log4j/trunk/tests/src/java/org/apache/log4j/PropertyConfiguratorTest.java?rev=697315&r1=697314&r2=697315&view=diff
==============================================================================
--- logging/log4j/trunk/tests/src/java/org/apache/log4j/PropertyConfiguratorTest.java (original)
+++ logging/log4j/trunk/tests/src/java/org/apache/log4j/PropertyConfiguratorTest.java Fri Sep 19 19:37:56 2008
@@ -27,7 +27,10 @@
 import org.apache.log4j.spi.OptionHandler;
 import org.apache.log4j.spi.Filter;
 import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.spi.ThrowableRenderer;
+import org.apache.log4j.spi.ThrowableRendererSupport;
 import org.apache.log4j.varia.LevelRangeFilter;
+import org.apache.log4j.helpers.OptionConverter;
 
 /**
  * Test property configurator.
@@ -267,4 +270,51 @@
         LogManager.resetConfiguration();
     }
 
+
+    /**
+     * Mock ThrowableRenderer for testThrowableRenderer.  See bug 45721.
+     */
+    public static class MockThrowableRenderer implements ThrowableRenderer, OptionHandler {
+        private boolean activated = false;
+        private boolean showVersion = true;
+
+        public MockThrowableRenderer() {
+        }
+
+        public void activateOptions() {
+            activated = true;
+        }
+
+        public boolean isActivated() {
+            return activated;
+        }
+
+        public String[] doRender(final Throwable t) {
+            return new String[0];
+        }
+
+        public void setShowVersion(boolean v) {
+            showVersion = v;
+        }
+
+        public boolean getShowVersion() {
+            return showVersion;
+        }
+    }
+
+    /**
+     * Test of log4j.throwableRenderer support.  See bug 45721.
+     */
+    public void testThrowableRenderer() {
+        Properties props = new Properties();
+        props.put("log4j.throwableRenderer", "org.apache.log4j.PropertyConfiguratorTest$MockThrowableRenderer");
+        props.put("log4j.throwableRenderer.showVersion", "false");
+        PropertyConfigurator.configure(props);
+        ThrowableRendererSupport repo = (ThrowableRendererSupport) LogManager.getLoggerRepository();
+        MockThrowableRenderer renderer = (MockThrowableRenderer) repo.getThrowableRenderer();
+        LogManager.resetConfiguration();
+        assertNotNull(renderer);
+        assertEquals(true, renderer.isActivated());
+        assertEquals(false, renderer.getShowVersion());
+    }
 }

Modified: logging/log4j/trunk/tests/src/java/org/apache/log4j/xml/DOMTestCase.java
URL: http://svn.apache.org/viewvc/logging/log4j/trunk/tests/src/java/org/apache/log4j/xml/DOMTestCase.java?rev=697315&r1=697314&r2=697315&view=diff
==============================================================================
--- logging/log4j/trunk/tests/src/java/org/apache/log4j/xml/DOMTestCase.java (original)
+++ logging/log4j/trunk/tests/src/java/org/apache/log4j/xml/DOMTestCase.java Fri Sep 19 19:37:56 2008
@@ -24,9 +24,13 @@
 import org.apache.log4j.LogManager;
 import org.apache.log4j.Logger;
 import org.apache.log4j.VectorAppender;
+import org.apache.log4j.PropertyConfigurator;
 import org.apache.log4j.spi.ErrorHandler;
 import org.apache.log4j.spi.LoggerFactory;
 import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.spi.ThrowableRenderer;
+import org.apache.log4j.spi.OptionHandler;
+import org.apache.log4j.spi.ThrowableRendererSupport;
 import org.apache.log4j.util.Compare;
 import org.apache.log4j.util.ControlFilter;
 import org.apache.log4j.util.Filter;
@@ -36,6 +40,8 @@
 import org.apache.log4j.util.SunReflectFilter;
 import org.apache.log4j.util.Transformer;
 
+import java.util.Properties;
+
 public class DOMTestCase extends TestCase {
 
   static String TEMP_A1 = "output/temp.A1";
@@ -332,4 +338,48 @@
       assertEquals("output/subst-test.A1", file);
   }
 
+    /**
+     * Mock ThrowableRenderer for testThrowableRenderer.  See bug 45721.
+     */
+    public static class MockThrowableRenderer implements ThrowableRenderer, OptionHandler {
+        private boolean activated = false;
+        private boolean showVersion = true;
+
+        public MockThrowableRenderer() {
+        }
+
+        public void activateOptions() {
+            activated = true;
+        }
+
+        public boolean isActivated() {
+            return activated;
+        }
+
+        public String[] doRender(final Throwable t) {
+            return new String[0];
+        }
+
+        public void setShowVersion(boolean v) {
+            showVersion = v;
+        }
+
+        public boolean getShowVersion() {
+            return showVersion;
+        }
+    }
+
+    /**
+     * Test of log4j.throwableRenderer support.  See bug 45721.
+     */
+    public void testThrowableRenderer1() {
+        DOMConfigurator.configure("input/xml/throwableRenderer1.xml");
+        ThrowableRendererSupport repo = (ThrowableRendererSupport) LogManager.getLoggerRepository();
+        MockThrowableRenderer renderer = (MockThrowableRenderer) repo.getThrowableRenderer();
+        LogManager.resetConfiguration();
+        assertNotNull(renderer);
+        assertEquals(true, renderer.isActivated());
+        assertEquals(false, renderer.getShowVersion());
+    }
+
 }



---------------------------------------------------------------------
To unsubscribe, e-mail: log4j-dev-unsubscribe@logging.apache.org
For additional commands, e-mail: log4j-dev-help@logging.apache.org