我们如何在Java中将行号打印到日志中

时间:2020-03-06 14:32:28  来源:igfitidea点击:

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

其他选择可能类似于在打印到日志时手动包括行号。还有其他办法吗?

解决方案

从Angsuman Chakraborty:

/** 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.logging包或者log4j中的日志记录框架。使用这些软件包,我们可以配置日志记录信息以包括上下文,直到类名。这样,每个日志消息将具有足够的唯一性,以知道它来自何处。结果,代码将包含一个我们通过调用的" logger"变量

logger.debug("a really descriptive message")

代替

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

我建议使用日志工具包,例如log4j。可以在运行时通过属性文件配置日志记录,并且我们可以打开/关闭行号/文件名日志记录等功能。

查看用于PatternLayout的javadoc,将为我们提供%L的选项的完整列表。

Log4J允许我们将行号作为其输出模式的一部分。有关如何执行此操作的详细信息,请参见http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/PatternLayout.html(转换模式中的关键元素为" L")。但是,Javadoc确实包括以下内容:

WARNING Generating caller location
  information is extremely slow. It's
  use should be avoided unless execution
  speed is not an issue.

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