35

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() 方法调用之前被删除或添加。

4

5 回答 5

52

您可以创建一个Throwable并使用它的StackTraceElements

  System.err.println(new Throwable().getStackTrace()[0].getLineNumber());

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

  System.err.println(Thread.currentThread().getStackTrace()[1].getLineNumber());

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

另请注意@Joachim 的回答中有关日志记录和性能的评论。

于 2013-07-04T14:57:24.450 回答
16

首先:如果有的话,那么日志模式(或布局器,或您的日志框架调用该部分的任何内容)应该执行此操作。代码中的记录器调用应该写入实际的业务信息。记录器应添加的位置信息。

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

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

于 2013-07-04T14:57:36.423 回答
14

我能够使用 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;
}

希望这可以帮助!我把它们放在一个实用程序类中,这样它们就不会碍事,但仍然很容易访问。第二种方法是私有的,以防止除第一种方法之外的任何其他方法调用它,以便它始终正常工作。

于 2014-10-16T17:29:40.637 回答
6

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

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

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

于 2019-02-27T10:03:23.597 回答
3

还可以使用以下代码段获取异常行号。在您的 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()));
    }

希望这有效!:)

于 2018-01-19T11:45:22.660 回答