tags:

views:

4224

answers:

9

How to print line numbers to the log. Say when outputting some information to the log, I also want to print the line number where that output is in the source code. As we can see in the stack trace, it displays the line number where the exception has occurred. Stack trace is available on the exception object.

Other alternative could be like manually including the line number when printing to the log. Is there any other way?

+4  A: 

From this guy:

/** Get the current line number.
 * @return int - Current line number.
 */
public static int getLineNumber() {
    return Thread.currentThread().getStackTrace()[2].getLineNumber();
}
Simon Buchan
This will always return the line number of the return statement in the called method and not necessarily the line number of the method call.
Ron Tuffin
Does not the [2] get the frame above getLineNumber()? ([1] being getLineNumber(), and [0] being getStackTrace(), presumably)
Simon Buchan
I played around a bit and if you use blah.getStackTrace[3].getLineNumber() as the method body it returns the line number of where the method was called.
Ron Tuffin
Thats just weird.
Simon Buchan
The index will change based on the JVM version. I believe it changed from 1.4 to 1.5.
Ed Thomas
+1  A: 

You can't guarantee line number consistency with code, especially if it is compiled for release. I would not recommend using line numbers for that purpose anyway, it would be better to give a payload of the place where the exception was raised (the trivial method being to set the message to include the details of the method call).

You might like to look at exception enrichment as a technique to improve exception handling http://tutorials.jenkov.com/java-exception-handling/exception-enrichment.html

UberAlex
+3  A: 

The code posted by @simon.buchan will work...

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

But if you call it in a method it will always return the line number of the line in the method so rather use the code snippet inline.

Ron Tuffin
I guessed the '2' was to get the line number of getLineNumber() caller.
Simon Buchan
@simon.buchan - edit your answer (as per my last comment). I don't want to steal your rep for your answer.
Ron Tuffin
+1  A: 

If it's been compiled for release this isn't possible. You might want to look into something like Log4J which will automatically give you enough information to determine pretty closely where the logged code occurred.

Greg
+8  A: 

I am compelled to answer by not answering your question. I'm assuming that you are looking for the line number solely to support debugging. There are better ways. There are hackish ways to get the current line. All I've seen are slow. You are better off using a logging framework like that in java.util.logging package or log4j. Using these packages you can configure your logging information to include context down to the class name. Then each log message would be unique enough to know where it came from. As a result, your code will have a 'logger' variable that you call via

logger.debug("a really descriptive message")

instead of

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

James A Wilson
+1  A: 

I would recommend using a logging toolkit such as log4j. Logging is configurable via properties files at runtime, and you can turn on / off features such as line number / filename logging.

Looking at the javadoc for the PatternLayout gives you the full list of options - what you're after is %L.

Phill Sacre
+4  A: 

Log4J allows you to include the line number as part of its output pattern. See http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/PatternLayout.html for details on how to do this (the key element in the conversion pattern is "L"). However, the Javadoc does include the following:

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

Jim Kiley
+1  A: 

first the general method (in an utility class, in plain old java1.4 code though, you may have to rewrite it for java1.5 and more)

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

Then the specific utility method to get the right 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;
  }
VonC
A: 

Use Log4J. Period

Vihung
Nice explanation…
Nicolas