我们如何打印行号到日志在 java

如何将行号打印到日志中。例如,在向日志输出一些信息时,我还想打印输出在源代码中的行号。正如我们在堆栈跟踪中看到的,它显示发生异常的行号。堆栈跟踪在异常对象上可用。

其他的替代方法可以是在打印到日志时手动包括行号。还有其他的方法吗?

166517 次浏览

来自 安格苏曼 · 查克拉博蒂(存档) :

/** Get the current line number.
* @return int - Current line number.
*/
public static int getLineNumber() {
return Thread.currentThread().getStackTrace()[2].getLineNumber();
}

您不能保证代码行号的一致性,特别是如果它是为发布而编译的。无论如何,我都不建议使用行号,最好给出引发异常的位置的有效负载(简单的方法是将消息设置为包含方法调用的细节)。

您可能希望将异常充实视为改进异常处理的一种技术 Http://tutorials.jenkov.com/java-exception-handling/exception-enrichment.html

@ simon.buchan 发布的代码可以工作..。

Thread.currentThread().getStackTrace()[2].getLineNumber()

但是如果在方法中调用它,它总是返回方法中行的行号,所以宁愿使用内联代码段。

如果是为了发布而编译的,这是不可能的。您可能需要查看类似 Log4J 的内容,它将自动提供足够的信息,以便非常准确地确定记录的代码发生在哪里。

我不得不回答你的问题。我假设您只是为了支持调试而寻找行号。还有更好的办法。有很多骇客式的方法可以获取当前行。我所看到的都是缓慢的。您最好使用类似 java.util.log 包或 Log4j中的日志框架。使用这些包,您可以将日志信息配置为包含上下文到类名。然后每个日志消息都是唯一的,足以知道它来自哪里。因此,您的代码将有一个‘ logger’变量,您可以通过它调用

logger.debug("a really descriptive message")

而不是

System.out.println("a really descriptive message")

我建议使用诸如 Log4j之类的日志记录工具包。可以在运行时通过属性文件配置日志记录,还可以打开/关闭行号/文件名日志记录等特性。

查看 图案设计的 javadoc 可以看到完整的选项列表——您需要的是% L。

Log4J 允许您将行号作为其输出模式的一部分。有关如何做到这一点的详细信息,请参阅 http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/PatternLayout.html(转换模式中的关键元素是“ L”)。不过,Javadoc 确实包括以下内容:

生成呼叫者位置 信息非常缓慢 除非执行,否则应避免使用 速度不是问题。

首先是通用方法(在实用程序类中,在普通的旧 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;
}

我们最终使用了这样一个自定义类来完成我们的 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

这是我们使用的日志记录器。

它包装了 Android Logger 并显示类名、方法名和行号。

Http://www.hautelooktech.com/2011/08/15/android-logging/

看看 这个链接。在这个方法中,当你双击 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;
}
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();
}

我的方式对我有用

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;
}

这些都可以得到当前线程和方法的行号,如果您在预期异常的地方使用 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]来获取冒犯者。

我使用这个小方法输出调用它的方法的跟踪和行号。

 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] + " " );
}

您可以使用-> Reporter.log (“”) ;

下面的代码是测试日志记录行的代码,没有类名和方法名,从这里调用日志记录方法

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;
}
}

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);
}

这正是我在这个库中实现的特性 XDDLib (但是,它是为 Android 设计的)

Lg.d("int array:", intArrayOf(1, 2, 3), "int list:", listOf(4, 5, 6))

enter image description here

单击 带下划线的文字导航到 log 命令所在的位置

StackTraceElement由此库外的第一个元素决定。因此,在这个库之外的任何地方都是合法的,包括 lambda expressionstatic initialization block等等。

对于任何想知道的人来说,getStackTrace()[3]方法中的索引表示触发线程传递到实际。不包括执行行的 getStackTrace ()方法。

这意味着,如果从3个嵌套方法 以上执行 Thread.currentThread().getStackTrace()[X].getLineNumber();行,则索引号必须是 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 教育的人,这对编译器的工作方式有着开阔的思路。

这是打印行号的代码。

Thread.currentThread().getStackTrace()[2].getLineNumber()

创建一个全局公共静态方法,使打印日志更加容易。

  public static void Loge(Context context, String strMessage, int strLineNumber) {
    

Log.e(context.getClass().getSimpleName(), strLineNumber + " : " + strMessage);
  

}