2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 package org.apache.log4j;
19 import org.apache.log4j.spi.ThrowableRenderer;
22 import java.lang.reflect.Method;
24 import java.security.CodeSource;
25 import java.util.HashMap;
29 * Enhanced implementation of ThrowableRenderer. Uses Throwable.getStackTrace
30 * if running on JDK 1.4 or later and delegates to DefaultThrowableRenderer.render
31 * on earlier virtual machines.
35 public final class EnhancedThrowableRenderer implements ThrowableRenderer {
37 * Throwable.getStackTrace() method.
39 private Method getStackTraceMethod;
41 * StackTraceElement.getClassName() method.
43 private Method getClassNameMethod;
47 * Construct new instance.
49 public EnhancedThrowableRenderer() {
51 Class[] noArgs = null;
52 getStackTraceMethod = Throwable.class.getMethod("getStackTrace", noArgs);
53 Class ste = Class.forName("java.lang.StackTraceElement");
54 getClassNameMethod = ste.getMethod("getClassName", noArgs);
55 } catch(Exception ex) {
62 public String[] doRender(final Throwable throwable) {
63 if (getStackTraceMethod != null) {
65 Object[] noArgs = null;
66 Object[] elements = (Object[]) getStackTraceMethod.invoke(throwable, noArgs);
67 String[] lines = new String[elements.length + 1];
68 lines[0] = throwable.toString();
69 Map classMap = new HashMap();
70 for(int i = 0; i < elements.length; i++) {
71 lines[i+1] = formatElement(elements[i], classMap);
74 } catch(Exception ex) {
77 return DefaultThrowableRenderer.render(throwable);
81 * Format one element from stack trace.
82 * @param element element, may not be null.
83 * @param classMap map of class name to location.
84 * @return string representation of element.
86 private String formatElement(final Object element, final Map classMap) {
87 StringBuffer buf = new StringBuffer("\tat ");
90 String className = getClassNameMethod.invoke(element, (Object[]) null).toString();
91 Object classDetails = classMap.get(className);
92 if (classDetails != null) {
93 buf.append(classDetails);
95 Class cls = findClass(className);
96 int detailStart = buf.length();
99 CodeSource source = cls.getProtectionDomain().getCodeSource();
100 if (source != null) {
101 URL locationURL = source.getLocation();
102 if (locationURL != null) {
106 if ("file".equals(locationURL.getProtocol())) {
107 String path = locationURL.getPath();
110 // find the last file separator character
112 int lastSlash = path.lastIndexOf('/');
113 int lastBack = path.lastIndexOf(File.separatorChar);
114 if (lastBack > lastSlash) {
115 lastSlash = lastBack;
118 // if no separator or ends with separator (a directory)
119 // then output the URL, otherwise just the file name.
121 if (lastSlash <= 0 || lastSlash == path.length() - 1) {
122 buf.append(locationURL);
124 buf.append(path.substring(lastSlash + 1));
128 buf.append(locationURL);
132 } catch(SecurityException ex) {
135 Package pkg = cls.getPackage();
137 String implVersion = pkg.getImplementationVersion();
138 if (implVersion != null) {
139 buf.append(implVersion);
143 classMap.put(className, buf.substring(detailStart));
145 } catch(Exception ex) {
147 return buf.toString();
151 * Find class given class name.
152 * @param className class name, may not be null.
153 * @return class, will not be null.
154 * @throws ClassNotFoundException thrown if class can not be found.
156 private Class findClass(final String className) throws ClassNotFoundException {
158 return Thread.currentThread().getContextClassLoader().loadClass(className);
159 } catch (ClassNotFoundException e) {
161 return Class.forName(className);
162 } catch (ClassNotFoundException e1) {
163 return getClass().getClassLoader().loadClass(className);