剛好看到關於log的討論。想起之前調查的一個問題。整理出來,但願對你們能有所幫助。
Sun JDK 源代碼下載 http://wwws.sun.com/software/communitysource/
先註冊並登陸到「Sun Community Source Licensing」,而後下載J2SE(幾十兆)或者J2EE(幾百兆)。
Log可以把代碼運行時間,類名,方法名,還有信息,所有都打印出來。
一個直觀的例子,每次啓動Tomcat(缺省配置)的時候。通常能夠看到
Jul 9, 2004 11:22:29 AM org.apache.struts.util.PropertyMessageResources <init>
INFO: Initializing, config='org.apache.webapp.admin.ApplicationResources', returnNull=true
Jul 9, 2004 11:22:41 AM org.apache.coyote.http11.Http11Protocol start
INFO: Starting Coyote HTTP/1.1 on port 8080
時間,類名,方法名,信息都打印了出來。
那麼,log是如何獲取調用自身的這個類和這個方法名的呢?
後面給出的代碼是JDK1.4的源代碼,和Log4J的源代碼。說明其實現原理。
得到調用類,和方法名,就是須要得到當前運行棧的結構。
new Throwable().getStackTrace() 會返回當前運行棧的結構層次。
利用這種原理,能夠得到整個運行棧的調用關係。
JDK1.4的java.util.logging包, 經過Throwable.getStackTrace()方法實現的。
// Get the stack trace.
StackTraceElement stack[] = (new Throwable()).getStackTrace();
完整的代碼在JDK1.4的源代碼裏面,java.util.logging.LogRecord類的inferCaller方法。
java
// Private method to infer the caller's class and method names private void inferCaller() { needToInferCaller = false; // Get the stack trace. StackTraceElement stack[] = (new Throwable()).getStackTrace(); // First, search back to a method in the Logger class. int ix = 0; while (ix < stack.length) { StackTraceElement frame = stack[ix]; String cname = frame.getClassName(); if (cname.equals("java.util.logging.Logger")) { break; } ix++; } // 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")) { // We've found the relevant frame. setSourceClassName(cname); setSourceMethodName(frame.getMethodName()); return; } ix++; } // We haven't found a suitable frame, so just punt. This is // OK as we are only commited to making a "best effort" here. }
Log4j有殊途同歸之妙。
org.apache.log4j.spi.LocationInfo類。
先用Throwable.printStackTrace()方法把Exception信息打印到一個字符串裏。
而後按行分析這個字符串。抽出調用類和方法。參見LocationInfo類的構造函數。
web
/** Instantiate location information based on a Throwable. We expect the Throwable <code>t</code>, to be in the format <pre> 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) ... </pre> <p>However, we can also deal with JIT compilers that "lose" the location information, especially between the parentheses. */ public LocationInfo(Throwable t, String fqnOfCallingClass)
e.printStackTrace()把Exception發生當時的整個運行棧結構展開,打印出來。
Log4J就是分析這個打印結果,得到全部的調用層次。
關於直接獲取調用類名的方法。
咱們來看sun.reflect.Reflection的getCallerClass()方法的說明。
apache
/** Returns the class of the method <code>realFramesToSkip</code> frames up the stack (zero-based), ignoring frames associated with java.lang.reflect.Method.invoke() and its implementation. The first frame is that associated with this method, so <code>getCallerClass(0)</code> returns the Class object for sun.reflect.Reflection. Frames associated with java.lang.reflect.Method.invoke() and its implementation are completely ignored and do not count toward the number of "real" frames skipped. */ public static native Class getCallerClass(int realFramesToSkip);
這是一個native方法。原理也是根據StackFrame(運行棧)獲取相應類的信息。這個方法直接返回一個Class名字,直接有效。參數realFramesToSkip用來選取你所須要的Stack層次,因此,你能夠用這個方法得到任何層次的上的調用類名。
Throwable.getStackTrace()也是一個native方法。原理也是根據StackFrame(運行棧)獲取相應類的信息。返回一個StackTraceElement[]。
StackTraceElement類在JDK1.4的java.lang的包裏面。裏面包含豐富的信息,很是適合Debug。
StackTraceElement類有以下方法:
getFileName(),getLineNumber(), getClassName(), getMethodName()。app