C語言中有__FILE__、__LINE__等預定義宏,用於獲取當前文件名和行號等信息,而且它們的值在預處理時就已經確定了,不會占用運行時時間去計算,這對打印日志相當有用。那么,Java語言是否也有類似的功能呢?
Java是否提供某種方法:可以讓用戶代碼在編譯時確定源碼行號等信息,本人暫時不知曉。不過從網上搜索得到的方法大致是:
- Thread.currentThread().getStackTrace()[1].getFileName():獲取當前文件名;
- Thread.currentThread().getStackTrace()[1].getLineNumber():獲取當前行號。
其中:Thread.currentThread().getStackTrace()返回的是一個數組形式的函數調用棧(棧頂在索引0處),其中第1個元素(索引為0)為最新調用的函數信息(getStackTrace()),第2個元素(索引為1)為當前函數(即調用getStackTrace()的函數)信息。示例:
1 public class Test { 2 public static void main(String args[]) { 3 StackTraceElement[] stack = Thread.currentThread().getStackTrace(); 4 for (int i = 0; i < stack.length; ++i) 5 System.out.println(stack[i].getFileName() + ":" + stack[i].getLineNumber() + ": " + stack[i].getMethodName()); 6 System.out.println(""); 7 foo(); 8 } 9 static void foo() { 10 StackTraceElement[] stack = Thread.currentThread().getStackTrace(); 11 for (int i = 0; i < stack.length; ++i) 12 System.out.println(stack[i].getFileName() + ":" + stack[i].getLineNumber() + ": " + stack[i].getMethodName()); 13 System.out.println(""); 14 bar(); 15 } 16 static void bar() { 17 StackTraceElement[] stack = Thread.currentThread().getStackTrace(); 18 for (int i = 0; i < stack.length; ++i) 19 System.out.println(stack[i].getFileName() + ":" + stack[i].getLineNumber() + ": " + stack[i].getMethodName()); 20 } 21 }
輸出:
$ javac Test.java $ java Test null:-1: getStackTrace Test.java:3: main null:-1: getStackTrace Test.java:10: foo Test.java:7: main null:-1: getStackTrace Test.java:17: bar Test.java:14: foo Test.java:7: main
於是,如果我們想在日志中打印當前文件名和行號,就可以:
1 public class Test { 2 public static void main(String args[]) { 3 System.out.println(Thread.currentThread().getStackTrace()[1].getFileName() + ":" + Thread.currentThread().getStackTrace()[1].getLineNumber()); 4 foo(); 5 } 6 static void foo() { 7 System.out.println(Thread.currentThread().getStackTrace()[1].getFileName() + ":" + Thread.currentThread().getStackTrace()[1].getLineNumber()); 8 bar(); 9 } 10 static void bar() { 11 System.out.println(Thread.currentThread().getStackTrace()[1].getFileName() + ":" + Thread.currentThread().getStackTrace()[1].getLineNumber()); 12 } 13 }
輸出:
$ javac Test.java $ java Test Test.java:3 Test.java:7 Test.java:11
不過每次都要寫這么長的幾串代碼“Thread.currentThread().getStackTrace()[1].getXXX()”拼一起看起來難看敲起來也累。於是我們可以將getStackTrace()封裝到另一個函數中:
1 public class Test { 2 public static void main(String args[]) { 3 System.out.println(__FILE__() + ":" + __LINE__()); 4 foo(); 5 } 6 static void foo() { 7 System.out.println(__FILE__() + ":" + __LINE__()); 8 bar(); 9 } 10 static void bar() { 11 System.out.println(__FILE__() + ":" + __LINE__()); 12 } 13 static String __FILE__() { 14 return Thread.currentThread().getStackTrace()[2].getFileName(); 15 } 16 static int __LINE__() { 17 return Thread.currentThread().getStackTrace()[2].getLineNumber(); 18 } 19 }
輸出結果一樣:
$ javac Test.java $ java Test Test.java:3 Test.java:7 Test.java:11
參考:
- http://stackoverflow.com/questions/22677815/eclipse-println-line-does-such-a-preprocessor-macro-exist-to-program
- http://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html#getStackTrace--
- http://docs.oracle.com/javase/8/docs/api/java/lang/StackTraceElement.html
- http://stackoverflow.com/questions/115008/how-can-we-print-line-numbers-to-the-log-in-java
- http://www.codeceo.com/article/8-java-log-framework.html