ログに行番号を出力する方法。ログに情報を出力するときに、ソースコード内でその出力がある行番号も出力したいとします。スタックトレースで確認できるように、例外が発生した行番号が表示されます。スタックトレースは、例外オブジェクトで利用できます。
他の方法としては、ログに印刷するときに行番号を手動で含めるようなものがあります。他の方法はありますか?
/** Get the current line number.
* @return int - Current line number.
*/
public static int getLineNumber() {
return Thread.currentThread().getStackTrace()[2].getLineNumber();
}
Androidの作業にこのようなカスタムクラスを使用することになりました。
import Android.util.Log;
public class DebugLog {
public final static boolean DEBUG = true;
public static void log(String message) {
if (DEBUG) {
String fullClassName = Thread.currentThread().getStackTrace()[2].getClassName();
String className = fullClassName.substring(fullClassName.lastIndexOf(".") + 1);
String methodName = Thread.currentThread().getStackTrace()[2].getMethodName();
int lineNumber = Thread.currentThread().getStackTrace()[2].getLineNumber();
Log.d(className + "." + methodName + "():" + lineNumber, message);
}
}
}
早くて汚い方法:
System.out.println("I'm in line #" +
new Exception().getStackTrace()[0].getLineNumber());
いくつかの詳細:
StackTraceElement l = new Exception().getStackTrace()[0];
System.out.println(
l.getClassName()+"/"+l.getMethodName()+":"+l.getLineNumber());
これは次のようなものを出力します:
com.example.mytest.MyClass/myMethod:103
私はあなたの質問に答えないことで答えざるを得ません。デバッグをサポートするためだけに行番号を探していると仮定しています。より良い方法があります。現在の行を取得するハック的な方法があります。私が見たすべては遅いです。 Java.util.loggingパッケージまたは log4j のようなロギングフレームワークを使用する方が良いでしょう。これらのパッケージを使用して、コンテキストをクラス名まで含めるようにロギング情報を構成できます。その後、各ログメッセージは、それがどこから来たのかを知るのに十分な一意性を持ちます。その結果、コードには「ロガー」変数があり、これを介して呼び出します
logger.debug("a really descriptive message")
の代わりに
System.out.println("a really descriptive message")
Log4Jでは、出力パターンの一部として行番号を含めることができます。これを行う方法の詳細については、 http://logging.Apache.org/log4j/1.2/apidocs/org/Apache/log4j/PatternLayout.html を参照してください(変換パターンの重要な要素は「 L ")。ただし、Javadocには次のものが含まれます。
警告発信者の位置情報の生成は非常に遅くなります。実行速度が問題にならない限り、その使用は避けるべきです。
この小さなメソッドを使用して、それを呼び出したメソッドのトレースと行番号を出力します。
Log.d(TAG, "Where did i put this debug code again? " + Utils.lineOut());
出力をダブルクリックして、そのソースコード行に移動します!
コードを配置する場所に応じて、レベル値を調整する必要がある場合があります。
public static String lineOut() {
int level = 3;
StackTraceElement[] traces;
traces = Thread.currentThread().getStackTrace();
return (" at " + traces[level] + " " );
}
@ simon.buchanが投稿したコードは動作します...
Thread.currentThread().getStackTrace()[2].getLineNumber()
ただし、メソッドで呼び出すと、メソッドの行の行番号が常に返されるため、コードスニペットをインラインで使用します。
log4j などのロギングツールキットを使用することをお勧めします。ロギングは、実行時にプロパティファイルを介して構成でき、行番号/ファイル名のロギングなどの機能をオン/オフにできます。
PatternLayout のjavadocを見ると、オプションの完全なリストが得られます。目的は%Lです。
stackLevel
は、このメソッドを呼び出す深さに依存します。 0から多数まで試して、どのような違いがあるかを確認してください。
stackLevel
が正当な場合、Java.lang.Thread.getStackTrace(Thread.Java:1536)
のような文字列を取得します
public static String getCodeLocationInfo(int stackLevel) {
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
if (stackLevel < 0 || stackLevel >= stackTraceElements.length) {
return "Stack Level Out Of StackTrace Bounds";
}
StackTraceElement stackTraceElement = stackTraceElements[stackLevel];
String fullClassName = stackTraceElement.getClassName();
String methodName = stackTraceElement.getMethodName();
String fileName = stackTraceElement.getFileName();
int lineNumber = stackTraceElement.getLineNumber();
return String.format("%s.%s(%s:%s)", fullClassName, methodName, fileName, lineNumber);
}
これはまさにこのlib XDDLib で実装した機能です。 (ただし、Android用です)
Lg.d("int array:", intArrayOf(1, 2, 3), "int list:", listOf(4, 5, 6))
下線付きテキストをクリックして、ログコマンドがある場所に移動します
そのStackTraceElement
は、このライブラリの外側の最初の要素によって決定されます。したがって、このライブラリの外部であれば、lambda expression
、static initialization block
などを含むどこでも有効です。
このリンク を見てください。その方法では、LogCatの行をダブルクリックすると、行コードにジャンプできます。
また、このコードを使用して行番号を取得できます。
public static int getLineNumber()
{
int lineNumber = 0;
StackTraceElement[] stackTraceElement = Thread.currentThread()
.getStackTrace();
int currentIndex = -1;
for (int i = 0; i < stackTraceElement.length; i++) {
if (stackTraceElement[i].getMethodName().compareTo("getLineNumber") == 0)
{
currentIndex = i + 1;
break;
}
}
lineNumber = stackTraceElement[currentIndex].getLineNumber();
return lineNumber;
}
これが私たちが使用するロガーです。
Android Loggerを囲み、クラス名、メソッド名、行番号を表示します。
private static final int CLIENT_CODE_STACK_INDEX;
static {
// Finds out the index of "this code" in the returned stack Trace - funny but it differs in JDK 1.5 and 1.6
int i = 0;
for (StackTraceElement ste : Thread.currentThread().getStackTrace()) {
i++;
if (ste.getClassName().equals(Trace.class.getName())) {
break;
}
}
CLIENT_CODE_STACK_INDEX = i;
}
private String methodName() {
StackTraceElement ste=Thread.currentThread().getStackTrace()[CLIENT_CODE_STACK_INDEX+1];
return ste.getMethodName()+":"+ste.getLineNumber();
}
特にリリース用にコンパイルされている場合は、コードと行番号の一貫性を保証できません。とにかくそのために行番号を使用することはお勧めしません。例外が発生した場所のペイロードを与える方が良いでしょう(簡単な方法は、メソッド呼び出しの詳細を含むようにメッセージを設定することです)。
例外処理を改善するための手法として、例外の強化を検討することをお勧めします http://tutorials.jenkov.com/Java-exception-handling/exception-enrichment.html
リリース用にコンパイルされている場合、これは不可能です。 Log4Jのようなものを調べると、ログに記録されたコードが発生した場所を非常に詳細に判断するのに十分な情報が自動的に得られます。
最初に一般的なメソッド(ユーティリティクラスで、プレーンな古いJava1.4コードでは、Java1.5以上に書き換える必要があるかもしれません)
/**
* Returns the first "[class#method(line)]: " of the first class not equal to "StackTraceUtils" and aclass. <br />
* Allows to get past a certain class.
* @param aclass class to get pass in the stack trace. If null, only try to get past StackTraceUtils.
* @return "[class#method(line)]: " (never empty, because if aclass is not found, returns first class past StackTraceUtils)
*/
public static String getClassMethodLine(final Class aclass) {
final StackTraceElement st = getCallingStackTraceElement(aclass);
final String amsg = "[" + st.getClassName() + "#" + st.getMethodName() + "(" + st.getLineNumber()
+")] <" + Thread.currentThread().getName() + ">: ";
return amsg;
}
次に、正しいstackElementを取得する特定のユーティリティメソッド:
/**
* Returns the first stack trace element of the first class not equal to "StackTraceUtils" or "LogUtils" and aClass. <br />
* Stored in array of the callstack. <br />
* Allows to get past a certain class.
* @param aclass class to get pass in the stack trace. If null, only try to get past StackTraceUtils.
* @return stackTraceElement (never null, because if aClass is not found, returns first class past StackTraceUtils)
* @throws AssertionFailedException if resulting statckTrace is null (RuntimeException)
*/
public static StackTraceElement getCallingStackTraceElement(final Class aclass) {
final Throwable t = new Throwable();
final StackTraceElement[] ste = t.getStackTrace();
int index = 1;
final int limit = ste.length;
StackTraceElement st = ste[index];
String className = st.getClassName();
boolean aclassfound = false;
if(aclass == null) {
aclassfound = true;
}
StackTraceElement resst = null;
while(index < limit) {
if(shouldExamine(className, aclass) == true) {
if(resst == null) {
resst = st;
}
if(aclassfound == true) {
final StackTraceElement ast = onClassfound(aclass, className, st);
if(ast != null) {
resst = ast;
break;
}
}
else
{
if(aclass != null && aclass.getName().equals(className) == true) {
aclassfound = true;
}
}
}
index = index + 1;
st = ste[index];
className = st.getClassName();
}
if(isNull(resst)) {
throw new AssertionFailedException(StackTraceUtils.getClassMethodLine() + " null argument:" + "stack trace should null"); //$NON-NLS-1$
}
return resst;
}
static private boolean shouldExamine(String className, Class aclass) {
final boolean res = StackTraceUtils.class.getName().equals(className) == false && (className.endsWith(LOG_UTILS
) == false || (aclass !=null && aclass.getName().endsWith(LOG_UTILS)));
return res;
}
static private StackTraceElement onClassfound(Class aclass, String className, StackTraceElement st) {
StackTraceElement resst = null;
if(aclass != null && aclass.getName().equals(className) == false)
{
resst = st;
}
if(aclass == null)
{
resst = st;
}
return resst;
}
これらはすべて、現在のスレッドとメソッドの行番号を取得します。例外が予想される場所でtry catchを使用すると、うまく機能します。ただし、未処理の例外をキャッチする場合は、デフォルトのキャッチされていない例外ハンドラを使用しており、現在のスレッドは、例外をスローしたクラスメソッドではなく、ハンドラ関数の行番号を返します。 Thread.currentThread()を使用する代わりに、例外ハンドラから渡されたThrowableを使用するだけです。
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
public void uncaughtException(Thread t, Throwable e) {
if(fShowUncaughtMessage(e,t))
System.exit(1);
}
});
上記では、ハンドラー関数(fShowUncaughtMessage)でe.getStackTrace()[0]を使用して、違反者を取得します。
以下のコードは、ロギングメソッドの呼び出し元のクラス名とメソッド名がないロギングラインのテストコードです
public class Utils {
/*
* debug variable enables/disables all log messages to logcat
* Useful to disable prior to app store submission
*/
public static final boolean debug = true;
/*
* l method used to log passed string and returns the
* calling file as the tag, method and line number prior
* to the string's message
*/
public static void l(String s) {
if (debug) {
String[] msg = trace(Thread.currentThread().getStackTrace(), 3);
Log.i(msg[0], msg[1] + s);
} else {
return;
}
}
/*
* l (tag, string)
* used to pass logging messages as normal but can be disabled
* when debug == false
*/
public static void l(String t, String s) {
if (debug) {
Log.i(t, s);
} else {
return;
}
}
/*
* trace
* Gathers the calling file, method, and line from the stack
* returns a string array with element 0 as file name and
* element 1 as method[line]
*/
public static String[] trace(final StackTraceElement e[], final int level) {
if (e != null && e.length >= level) {
final StackTraceElement s = e[level];
if (s != null) { return new String[] {
e[level].getFileName(), e[level].getMethodName() + "[" + e[level].getLineNumber() + "]"
};}
}
return null;
}
}