今天進行slf4j中logger的同步封裝。主要目的是為了以后方便更換日志實現系統。
遇到的問題:使用Thread.currentThread().getStackTrace()[1].getClassName()得到的是當前類而不是調用類。見以下代碼:
private org.slf4j.Logger logger = null;
/**
* construction method
*/
public Logger(){
// get the current class logger
logger = LoggerFactory.getLogger(Thread.currentThread().getStackTrace()[1].getClassName());
}
所以打印日志的時候並沒有得到日志真正所屬類的信息。
然后,我進行了一組測試:
測試A:
public class ThreadTest {
public static void TestString(){
StackTraceElement[] arr = new Exception().getStackTrace();
for(int i=0;i<=arr.length-1;i++){
System.out.println(arr[i].getClassName()+";"+arr[i].getMethodName()+";"+arr[i].getFileName());
}
}
}
public class App
{
public static void main( String[] args )
{
ThreadTest.TestString();
}
}
結果是:
test.ThreadTest;TestString;ThreadTest.java(當前方法和類)
test.App;main;App.java(調用該方法的方法和類)
測試B:
public class ThreadTest {
public static void TestString(){
StackTraceElement[] arr = Thread.currentThread().getStackTrace();
for(int i=0;i<=arr.length-1;i++){
System.out.println(arr[i].getClassName()+";"+arr[i].getMethodName()+";"+arr[i].getFileName());
}
}
}
App類同上。得到的結果是:
java.lang.Thread;getStackTrace;Thread.java(Thread的信息)
test.ThreadTest;TestString;ThreadTest.java(當前方法和類)
test.App;main;App.java(調用該方法的方法和類)
啥米情況???難道就是由於Thread的getStackTrace比Throwable(Exception的父類)的getStackTrace多打了一段Thread類的信息???
因為不確定是不是真的是這樣子的,故去查看了下jdk的源代碼(純屬裝X,不抱幻想)。
首先是Throwable的getStackTrace方法,代碼例如以下:
public StackTraceElement[] getStackTrace() {
return getOurStackTrace().clone();
}
private synchronized StackTraceElement[] getOurStackTrace() {
// Initialize stack trace field with information from
// backtrace if this is the first call to this method
if (stackTrace == UNASSIGNED_STACK ||
(stackTrace == null && backtrace != null) /* Out of protocol state */) {
int depth = getStackTraceDepth();
stackTrace = new StackTraceElement[depth];
for (int i=0; i < depth; i++)
stackTrace[i] = getStackTraceElement(i);
} else if (stackTrace == null) {
return UNASSIGNED_STACK;
}
return stackTrace;
}
貌似沒有什么不正確勁的地方哈,當中
int depth = getStackTraceDepth();
stackTrace[i] = getStackTraceElement(i);兩個方法都是native的,不予深究了。這里沒有找到問題。就去看了下Thread的getStackTrace方法,代碼例如以下:
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);
}
// optimization so we do not call into the vm for threads that
// have not yet started or have terminated
if (!isAlive()) {
return EMPTY_STACK_TRACE;
}
StackTraceElement[][] stackTraceArray = dumpThreads(new Thread[] {this});
StackTraceElement[] stackTrace = stackTraceArray[0];
// a thread that was alive during the previous isAlive call may have
// since terminated, therefore not having a stacktrace.
if (stackTrace == null) {
stackTrace = EMPTY_STACK_TRACE;
}
return stackTrace;
} else {
// Don't need JVM help for current thread
return (new Exception()).getStackTrace();
}
}
乍一看也沒啥錯啊~~~不正確,突然間跟着邏輯走一下。在if語句里面由於
this != Thread.currentThread()是為true的。所以方法會運行else里面的語句,里面是啥。!!
!
return (new Exception()).getStackTrace();All right!
就是這里,這里jvm去運行了new Exception().getStackTrace();就是這句話讓使用Thread的getStackTrace方法就有可能多打印一句java.lang.Thread;getStackTrace;Thread.java出來。這也就是為什么使用
logger = LoggerFactory.getLogger(Thread.currentThread().getStackTrace()[1].getClassName());會得不到正確的日志信息的原因了。
應該就是這樣子了。為了驗證我想的對不正確,寫了一個測試驗證的樣例,代碼例如以下:
public class TestException {
public static StackTraceElement[] getStackTrace() {
return new Exception().getStackTrace();
}
}
public class ThreadTest {
public static void TestString(){
StackTraceElement[] arr = TestException.getStackTrace();
for(int i=0;i<=arr.length-1;i++){
System.out.println(arr[i].getClassName()+";"+arr[i].getMethodName()+";"+arr[i].getFileName());
}
}
}
public class App
{
public static void main( String[] args )
{
ThreadTest.TestString();
}
}
運行之后的結果是:
test.TestException;getStackTrace;TestException.java
test.ThreadTest;TestString;ThreadTest.java
test.App;main;App.java
紅色的一行即證明了我的想法是正確的!
全部的驗證至此結束了。這個問題最后解決的方法非常easy,要么使用new Exception().getStackTrace()[1];要么使用Thread.currentThread().getStackTrace()[2]。
盡管這個問題非常小,也非常基礎。可是我還是非常興奮。畢竟粘上了源代碼,頓時逼格升高不少~~~
新手上路。高手饒命~!
