2008年(3500)
分类:
2008-05-04 19:30:28
t
, to be in the format
java.lang.Throwable ... at org.apache.log4j.PatternLayout.format(PatternLayout.java:413) at org.apache.log4j.FileAppender.doAppend(FileAppender.java:183) at org.apache.log4j.Category.callAppenders(Category.java:131) at org.apache.log4j.Category.log(Category.java:512) at callers.fully.qualified.className.methodName(FileName.java:74) ...*/ public LocationInfo(Throwable t, String fqnOfCallingClass) { String s; … t.printStackTrace(pw); s = sw.toString(); sw.getBuffer().setLength(0); …. // 这里的代码省略 } [/code] 这里我们可以看到整体的实现思路。 首先,t.printStackTrace(pw); 获得stack trace字符串。这个t是 new Throwable()的结果。用户程序调用Log4J方法之后,Log4J自己又进行了4次调用,然后才获得了 t = new Throwable() : at org.apache.log4j.PatternLayout.format(PatternLayout.java:413) at org.apache.log4j.FileAppender.doAppend(FileAppender.java:183) at org.apache.log4j.Category.callAppenders(Category.java:131) at org.apache.log4j.Category.log(Category.java:512) 那么,往下走4行,就可以回到用户程序本身的调用信息: at callers.fully.qualified.className.methodName(FileName.java:74) 这一行里面,类名、方法名、文件名、行号等信息全有了。解析这一行,就可以获得需要的所有信息。 三、JDK1.4 Log的相关实现 Log4J大获成功,Sun决定在JDK1.4中引入这个Log功能。 为了免去解析StackTrace字符串的麻烦,JDK1.4引入了一个新的类,StackTraceElement。 public final class StackTraceElement implements java.io.Serializable { // Normally initialized by VM (public constructor added in 1.5) private String declaringClass; private String methodName; private String fileName; private int lineNumber; 可以看到,恰好包括类名、方法名、文件名、行号等信息。 我们来看JDK1.4 Log的相关实现。 LocationInfo.java 的infoCaller方法(推算调用者) // Private method to infer the callers class and method names private void inferCaller() { … // Get the stack trace. StackTraceElement stack[] = (new Throwable()).getStackTrace(); // First, search back to a method in the Logger class. …. // 这里的代码省略 // Now search for the first frame before the "Logger" class. while (ix < stack.length) { StackTraceElement frame = stack[ix]; String cname = frame.getClassName(); if (!cname.equals("java.util.logging.Logger")) // Weve found the relevant frame. … // 这里的代码省略 } // We haven found a suitable frame, so just punt. This is // OK as we are only committed to making a "best effort" here. } 从注释中就可以看出实现思路。过程和Log4J异曲同工。只是免去了解析字符串的麻烦。 四、Log4J 1.3 alpha的相关实现 既然JDK1.4中引入了StackTraceElement类,Log4J也要与时俱进。LocationInfo类也有了相应的变化。 /** Instantiate location information based on a Throwable. We expect the Throwable
t
, to be in the format
java.lang.Throwable ... at org.apache.log4j.PatternLayout.format(PatternLayout.java:413) at org.apache.log4j.FileAppender.doAppend(FileAppender.java:183) at org.apache.log4j.Category.callAppenders(Category.java:131) at org.apache.log4j.Category.log(Category.java:512) at callers.fully.qualified.className.methodName(FileName.java:74) ...
However, we can also deal with JIT compilers that "lose" the location information, especially between the parentheses. */ public LocationInfo(Throwable t, String fqnOfInvokingClass) { if(PlatformInfo.hasStackTraceElement()) { StackTraceElementExtractor.extract(this, t, fqnOfInvokingClass); } else { LegacyExtractor.extract(this, t, fqnOfInvokingClass); } } 可以看到,Log4J首先判断Java平台是否支持StackTraceElement,如果是,那么用StackTraceElementExtractor,否则使用原来的LegacyExtractor。 下面来看StackTraceElementExtractor.java /** * A faster extractor based on StackTraceElements introduced in JDK 1.4. * * The present code uses reflection. Thus, it should compile on all platforms. * * @author Martin Schulz * @author Ceki Gülcü * */ public class StackTraceElementExtractor { protected static boolean haveStackTraceElement = false; private static Method getStackTrace = null; private static Method getClassName = null; private static Method getFileName = null; private static Method getMethodName = null; private static Method getLineNumber = null; …. // 以下代码省略 可以看到,Log4J 1.3仍然兼容JDK1.3,而且为JDK1.4也做了相应的优化。 五、JDK1.5的Thread Stack Trace JDK1.5在Thread类里面引入了getStackTrace()和getAllStackTraces()两个方法。这下子,我们不用 (new Throwable()).getStackTrace ();可以调用 Thread.getCurrentThread().getStackTrace()来获得当前线程的运行栈信息。不仅如此,只要权限允许,还可以获得其它线程的运行栈信息。 /** * Returns an array of stack trace elements representing the stack dump * of this thread. This method will return a zero-length array if * this thread has not started or has terminated. * If the returned array is of non-zero length then the first element of * the array represents the top of the stack, which is the most recent * method invocation in the sequence. The last element of the array * represents the bottom of the stack, which is the least recent method * invocation in the sequence. * *
If there is a security manager, and this thread is not * the current thread, then the security managers * checkPermission method is called with a * RuntimePermission("getStackTrace") permission * to see if its ok to get the stack trace. * *
Some virtual machines may, under some circumstances, omit one
* or more stack frames from the stack trace. In the extreme case,
* a virtual machine that has no stack trace information concerning
* this thread is permitted to return a zero-length array from this
* method.
*
* @return an array of StackTraceElement,
* each represents one stack frame.
*
* @since 1.5
*/
public StackTraceElement[] getStackTrace() {
if (this != Thread.currentThread()) {
// check for getStackTrace permission
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkPermission(
SecurityConstants.GET_STACK_TRACE_PERMISSION);
}
}
if (!isAlive()) {
return EMPTY_STACK_TRACE;
}
Thread[] threads = new Thread[1];
threads[0] = this;
StackTraceElement[][] result = dumpThreads(threads);
return result[0];
}
/**
* Returns a map of stack traces for all live threads.
*
* @since 1.5
*/
public static Map
线程运行栈信息的获取线程运行栈信息的获取线程运行栈信息的获取线程运行栈信息的获取线程运行栈信息的获取线程运行栈信息的获取线程运行栈信息的获取线程运行栈信息的获取线程运行栈信息的获取线程运行栈信息的获取线程运行栈信息的获取线程运行栈信息的获取