Java中有没有办法通过反射或一些很棒的API动态获取当前行号?就像异常发生时一样,行号在堆栈跟踪中打印出来,如下所示:
at weblogic.rmi.cluster.ClusterableRemoteRef.invoke(ClusterableRemoteRef.java:348)
现在有没有办法在下面的代码中打印或记录?
log.error("Error in: " + this.getClass.getName() + "at line #"+ this.getClass.getActualLine());
您可能会问,我为什么不简单地打印行号?好吧,因为代码可能会在特定的log.error()方法调用之前被删除或添加。
答案 0 :(得分:40)
您可以创建Throwable
并使用其StackTraceElements
:
System.err.println(new Throwable().getStackTrace()[0].getLineNumber());
正如@Joachim所说,您也可以使用Thread.getStackTrace()
,例如像
System.err.println(Thread.currentThread().getStackTrace()[1].getLineNumber());
请注意,第二种方法会返回一个稍微不同的数组 - 您需要使用索引为1的数组元素来获取当前行号,因为它包含对getStackTrace()
的调用本身就是第一个元素。
还要注意@Joachim的答案中关于记录和表现的评论。
答案 1 :(得分:16)
首先:如果任何,那么日志记录模式(或者外包,或者您的日志框架调用该部分的任何内容)都应该这样做。代码中的记录器调用应仅写入实际的业务信息。有关记录器应添加位置的信息。
接下来:获得这种操作的成本很高(就时间而言),因为Java并未针对此进行优化。在运行时,JVM需要检查其状态,加载/解析调试信息并找到与给定指令对应的行号。这就是为什么这种信息通常只在异常发生时给出(在这种情况下我们已经遇到问题并且知道所花费的时间通常是值得的。)
最后但并非最不重要:若由于某种原因您自己需要这些信息,您可以使用Thread.getStackTrace()
并检查其上的第二个StackTraceElement
。
答案 2 :(得分:9)
我能够使用Thread.currentThread()。getStackTrace()方法创建一组函数,这些函数一起工作以产生调用第一个方法的代码的行号,如下所示:
/** @return The line number of the code that ran this method
* @author Brian_Entei */
public static int getLineNumber() {
return ___8drrd3148796d_Xaf();
}
/** This methods name is ridiculous on purpose to prevent any other method
* names in the stack trace from potentially matching this one.
*
* @return The line number of the code that called the method that called
* this method(Should only be called by getLineNumber()).
* @author Brian_Entei */
private static int ___8drrd3148796d_Xaf() {
boolean thisOne = false;
int thisOneCountDown = 1;
StackTraceElement[] elements = Thread.currentThread().getStackTrace();
for(StackTraceElement element : elements) {
String methodName = element.getMethodName();
int lineNum = element.getLineNumber();
if(thisOne && (thisOneCountDown == 0)) {
return lineNum;
} else if(thisOne) {
thisOneCountDown--;
}
if(methodName.equals("___8drrd3148796d_Xaf")) {
thisOne = true;
}
}
return -1;
}
希望这有帮助!我将它们放在一个实用程序类中,以便它们不受影响,但仍然可以轻松访问。第二种方法是私有的,以防止除第一种方法之外的任何其他方法调用它,以便它始终正常工作。
答案 3 :(得分:2)
还可以使用以下代码段获取异常行号。在util类中使用以下util方法。
public static int getExceptionLineNumber(Exception e, String methodNameExp)
{
StackTraceElement ele = Arrays.stream(e.getStackTrace()).filter(o -> o.getMethodName().equals(methodNameExp)).findAny().orElse(null);
if(ele != null){
return ele.getLineNumber();
}
return -1;
}
从catch块你可以调用如下:
catch (Exception e){
logger.info("Exception line number "+ Util.getExceptionLineNumber(e, new Object(){}.getClass().getEnclosingMethod().getName()));
}
希望这有效! :)
答案 4 :(得分:1)
从JDK9开始,您可以使用StackWalker。
与使用getStackTrace()
相比,这使我的速度提高了约50%。
int get_line_number() {
List<StackFrame> stack = StackWalker.getInstance(StackWalker.Option.SHOW_HIDDEN_FRAMES).walk((s) -> s.skip(1).collect(Collectors.toList()));
return stack.get(0).getLineNumber();
}
我的信息流可以做得更好,但是我没有经验,而且我真的不喜欢信息流,所以随时编辑我的帖子以改善这一部分。