View | Details | Raw Unified | Return to bug 45721
Collapse All | Expand All

(-)tests/src/java/org/apache/log4j/spi/ThrowableInformationTest.java (-1 / +19 lines)
Lines 20-26 Link Here
20
20
21
import java.io.PrintWriter;
21
import java.io.PrintWriter;
22
22
23
import org.apache.log4j.config.PropertySetterException;
23
24
25
24
/**
26
/**
25
 * Unit tests for ThrowableInformation.
27
 * Unit tests for ThrowableInformation.
26
 */
28
 */
Lines 299-302 Link Here
299
        String[] rep2 = ti.getThrowableStrRep();
301
        String[] rep2 = ti.getThrowableStrRep();
300
        assertEquals("Hello, World", rep2[0]);
302
        assertEquals("Hello, World", rep2[0]);
301
    }
303
    }
302
}
304
305
    public void testStackTracePackageName() throws Exception {
306
        ThrowableInformation ti = new ThrowableInformation(
307
                new PropertySetterException("Hello"));
308
        String[] rep = ti.getThrowableStrRep();
309
        boolean found = false;
310
        for (int i = 0, size = rep.length; i < size; i++) {
311
            String line = rep[i];
312
            //System.out.println(line);
313
            if (line.indexOf("[") > 0) {
314
                found = true;
315
            }
316
        }
317
        assertTrue("Found a package version for the JDK", found);
318
    }
319
320
}
(-)src/main/java/org/apache/log4j/spi/ThrowableInformation.java (-2 / +141 lines)
Lines 19-24 Link Here
19
19
20
import java.io.*;
20
import java.io.*;
21
import java.util.ArrayList;
21
import java.util.ArrayList;
22
import java.net.URL;
22
23
23
/**
24
/**
24
  * ThrowableInformation is log4j's internal representation of
25
  * ThrowableInformation is log4j's internal representation of
Lines 40-45 Link Here
40
  private transient Throwable throwable;
41
  private transient Throwable throwable;
41
  private String[] rep;
42
  private String[] rep;
42
43
44
  private static boolean includeVersionInformation = true;
45
46
  /**
47
   * Returns true if version information should be included in stack traces
48
   */
49
  public static
50
  boolean isIncludeVersionInformation() {
51
    return includeVersionInformation;
52
  }
53
54
  /**
55
   * Allows the extra version information included in stack traces to be disabled
56
   */
57
  public static
58
  void setIncludeVersionInformation(boolean includeVersionInformation) {
59
    ThrowableInformation.includeVersionInformation = includeVersionInformation;
60
  }
61
43
  public
62
  public
44
  ThrowableInformation(Throwable throwable) {
63
  ThrowableInformation(Throwable throwable) {
45
    this.throwable = throwable;
64
    this.throwable = throwable;
Lines 75-81 Link Here
75
      try {
94
      try {
76
        String line = reader.readLine();
95
        String line = reader.readLine();
77
        while(line != null) {
96
        while(line != null) {
78
          lines.add(line);
97
          lines.add(formatExceptionLine(line));
79
          line = reader.readLine();
98
          line = reader.readLine();
80
        }
99
        }
81
      } catch(IOException ex) {
100
      } catch(IOException ex) {
Lines 90-95 Link Here
90
    }
109
    }
91
    return (String[]) rep.clone();
110
    return (String[]) rep.clone();
92
  }
111
  }
93
}
112
113
  /**
114
   * Appends the optional text [jarName:versionNumber] to each line of the stack trace to
115
   * indicate what name of jar the code comes from along with which version was found on the classpath
116
   */
117
  protected
118
  String formatExceptionLine(String line) {
119
    if (!isIncludeVersionInformation()) {
120
      return line;
121
    }
122
    String trimmed = line.trim();
123
    if (trimmed.startsWith("at ")) {
124
      // lets try append the package version to this line
125
      String classAndMethod = trimmed.substring(3).trim();
126
      int idx = classAndMethod.indexOf('(');
127
      if (idx > 0) {
128
        classAndMethod = classAndMethod.substring(0, idx);
129
      }
130
      idx = classAndMethod.lastIndexOf('.');
131
      String className = classAndMethod;
132
      String method = "";
133
      if (idx > 0) {
134
        method = classAndMethod.substring(idx + 1);
135
        className = className.substring(0, idx);
136
      }
137
      String packageName = "";
138
      idx = className.lastIndexOf('.');
139
      if (idx > 0) {
140
        packageName = className.substring(0, idx);
141
      }
142
      if (packageName.length() > 0) {
143
        String version = getVersion(packageName);
144
        String jarFile = getJarNameOfClass(className);
145
        if (notNullOrBlank(version)) {
146
          if (notNullOrBlank(jarFile)) {
147
            return line + " [" + jarFile + ":" + version + "]";
148
          }
149
          else {
150
            return line + " [" + version + "]";
151
          }
152
        }
153
        else if (notNullOrBlank(jarFile)) {
154
          return line + " [" + jarFile + "]";
155
        }
156
      }
157
    }
158
    return line;
159
  }
94
160
161
  protected static boolean notNullOrBlank(String text) {
162
    return text != null && text.length() > 0;
163
  }
95
164
165
  /**
166
   * Attempts to deduce the package version number of the given package
167
   * that is currently on the classpath
168
   */
169
  protected
170
  String getVersion(String packageName) {
171
    try {
172
      Package aPackage = Package.getPackage(packageName);
173
      if (aPackage != null) {
174
        return aPackage.getImplementationVersion();
175
      }
176
    } catch (Exception e) {
177
      // ignore exception
178
    }
179
    return "";
180
  }
181
182
  /**
183
   * Uses the context class path or the current global class loader to
184
   * deduce the file that the given class name comes from
185
   */
186
  protected
187
  String getJarNameOfClass(String className) {
188
    try {
189
      Class type = findClass(className);
190
      if (type != null) {
191
        URL resource = type.getClassLoader().getResource(type.getName().replace('.', '/') + ".class");
192
        if (resource != null) {
193
          String text = resource.toString();
194
          int idx = text.lastIndexOf('!');
195
          if (idx > 0) {
196
            text = text.substring(0, idx);
197
            // now lets remove all but the file name
198
            idx = text.lastIndexOf('/');
199
            if (idx > 0) {
200
              text = text.substring(idx + 1);
201
            }
202
            idx = text.lastIndexOf('\\');
203
            if (idx > 0) {
204
              text = text.substring(idx + 1);
205
            }
206
            return text;
207
          }
208
        }
209
      }
210
    }
211
    catch (Exception e) {
212
      // ignore
213
    }
214
    return "";
215
  }
216
217
  private Class findClass(String className) {
218
    try {
219
      return Thread.currentThread().getContextClassLoader().loadClass(className);
220
    } catch (ClassNotFoundException e) {
221
      try {
222
        return Class.forName(className);
223
      } catch (ClassNotFoundException e1) {
224
        try {
225
          return getClass().getClassLoader().loadClass(className);
226
        } catch (ClassNotFoundException e2) {
227
          return null;
228
        }
229
      }
230
    }
231
  }
232
}
233
234

Return to bug 45721