如何将行号打印到日志。说在向日志输出一些信息时,我还想打印输出在源代码中的行号。正如我们在堆栈跟踪中看到的,它显示了发生异常的行号。堆栈跟踪可用于异常对象。
其他替代方法可能是在打印到日志时手动包含行号。还有其他方法吗?
来自Angsuman Chakraborty(存档):
/** 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 确实包括以下内容:
警告生成呼叫者位置信息非常慢。除非执行速度不是问题,否则应避免使用它。
@simon.buchan 发布的代码将起作用...
Thread.currentThread().getStackTrace()[2].getLineNumber()
但是,如果您在方法中调用它,它将始终返回方法中行的行号,因此请使用内联代码片段。
我建议使用log4j之类的日志记录工具包。日志记录可在运行时通过属性文件进行配置,您可以打开/关闭行号/文件名日志记录等功能。
查看PatternLayout的 javadoc 可以为您提供完整的选项列表 - 您所追求的是 %L。
我使用这个小方法输出调用它的方法的跟踪和行号。
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] + " " );
}
您无法保证行号与代码的一致性,尤其是在为发布而编译的情况下。无论如何,我不建议为此目的使用行号,最好提供引发异常的位置的有效负载(简单的方法是将消息设置为包含方法调用的详细信息)。
您可能希望将异常丰富视为一种改进异常处理的技术 http://tutorials.jenkov.com/java-exception-handling/exception-enrichment.html
这些都可以为您提供当前线程和方法的行号,如果您在预期异常的地方使用 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] 来获取违规者。
如果它是为发布而编译的,这是不可能的。您可能想研究类似 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;
}
看看这个链接。在该方法中,当您双击 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();
}
对于任何想知道的人,getStackTrace()[3]
方法中的索引表示触发行在实际 .getStackTrace() 方法(不包括执行行)之前经过的线程数量。
这意味着如果从上面Thread.currentThread().getStackTrace()[X].getLineNumber();
的3 个嵌套方法执行该行,则索引号必须为3。
例子:
第一层
private static String message(String TAG, String msg) {
int lineNumber = Thread.currentThread().getStackTrace()[3].getLineNumber();
return ".(" + TAG + ".java:"+ lineNumber +")" + " " + msg;
}
第二层
private static void print(String s) {
System.out.println(s);
}
第三层
public static void normal(
String TAG,
String message
) {
print(
message(
TAG,
message
)
);
}
执行线:
Print.normal(TAG, "StatelessDispatcher");
作为一个没有接受过任何正式 IT 教育的人,这一直是关于编译器如何工作的想法。
下面的代码是记录行的测试代码,没有类名和调用记录方法的方法名
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;
}
}
这正是我在这个 lib XDDLib中实现的功能。(但是,它适用于安卓)
Lg.d("int array:", intArrayOf(1, 2, 3), "int list:", listOf(4, 5, 6))
单击带下划线的文本以导航到日志命令所在的位置
这StackTraceElement
是由这个库之外的第一个元素决定的。因此,此库之外的任何地方都是合法的,包括lambda expression
,static initialization block
等。
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);
}
我的方式对我有用
String str = "select os.name from os where os.idos="+nameid; try {
PreparedStatement stmt = conn.prepareStatement(str);
ResultSet rs = stmt.executeQuery();
if (rs.next()) {
a = rs.getString("os.n1ame");//<<<----Here is the ERROR
}
stmt.close();
} catch (SQLException e) {
System.out.println("error line : " + e.getStackTrace()[2].getLineNumber());
return a;
}
你可以使用 -> Reporter.log("");