动态获取当前行号

VSZ*_*SZM 30 java reflection

Java中有没有办法通过反射或一些很棒的API动态获取当前行号?就像异常发生时一样,行号在堆栈跟踪中打印出来,如下所示:

at weblogic.rmi.cluster.ClusterableRemoteRef.invoke(ClusterableRemoteRef.java:348)
Run Code Online (Sandbox Code Playgroud)

现在有没有办法在下面的代码中打印或记录?

log.error("Error in: " + this.getClass.getName() + "at line #"+ this.getClass.getActualLine());
Run Code Online (Sandbox Code Playgroud)

你可能会问,我为什么不简单地打印行号?好吧,因为代码可能会在特定的log.error()方法调用之前被删除或添加.

And*_*ter 41

您可以创建Throwable并使用它StackTraceElements:

  System.err.println(new Throwable().getStackTrace()[0].getLineNumber());
Run Code Online (Sandbox Code Playgroud)

正如@Joachim所说,你也可以使用Thread.getStackTrace(),例如喜欢

  System.err.println(Thread.currentThread().getStackTrace()[1].getLineNumber());
Run Code Online (Sandbox Code Playgroud)

请注意,第二种方法返回一个稍微不同的数组 - 您需要使用索引为1的数组元素来获取当前行号,因为它包含对getStackTrace()自身的调用作为第一个元素.

另请注意@Joachim的答案中关于记录和性能的评论.


Joa*_*uer 16

首先:如果有的话,那么日志模式(或者是layouter,或者你的日志框架调用那个部分的任何东西)应该这样做.代码中的记录器调用应该写入实际的业务信息.有关记录器应添加的位置的信息.

下一步:获得这种操作很昂贵(就时间而言),因为Java没有针对此进行优化.在运行时,JVM需要检查其状态,加载/解析调试信息并找到与给定指令对应的行号.这就是为什么这种信息通常只是在发生异常时给出的原因(在这种情况下我们已经遇到问题并且知道花费的时间通常是值得的).

最后但并非最不重要:如果由于某种原因你需要自己的信息,你可以使用Thread.getStackTrace()并检查第二个StackTraceElement.


Bri*_*tei 10

我能够使用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;
}
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助!我将它们放在一个实用程序类中,以便它们不受影响,但仍然可以轻松访问.第二种方法是私有的,以防止除第一种方法之外的任何其他方法调用它,以便它始终正常工作.


cla*_*l3r 5

从 JDK9 开始,您可以使用 StackWalker。与使用getStackTrace().

int get_line_number() {
    return StackWalker.getInstance(StackWalker.Option.SHOW_HIDDEN_FRAMES).walk(
      (s) -> s.skip(1).findFirst()).get().getLineNumber();
}
Run Code Online (Sandbox Code Playgroud)

我的直播可以做得更好,但我没有这方面的经验,而且我真的不喜欢直播,所以请随时编辑我的帖子以改进这部分。