[ACCEPTED]-Java Logging: show the source line number of the caller (not the logging helper method)-apache-commons-logging

Accepted answer
Score: 36

Alternative answer.

It is possible to ask 9 log4j to exclude the helper class by using 8 the method

Category.log(String callerFQCN, Priority 7 level, Object message, Throwable t)

and specifying 6 the helper class as 'callerFQCN'.

For example 5 here is a class using a helper:

public class TheClass {
    public static void main(String...strings) {
        LoggingHelper.log("Message using full log method in logging helper.");
        LoggingHelper.logNotWorking("Message using class info method");
}}

and the code 4 of the helper:

public class LoggingHelper {
private static Logger LOG = Logger.getLogger(LoggingHelper.class);

public static void log(String message) {
    LOG.log(LoggingHelper.class.getCanonicalName(), Level.INFO, message, null);
}

public static void logNotWorking(String message) {
    LOG.info(message);
} }

The first method will output 3 your expected result.

Line(TheClass.main(TheClass.java:4)) Message using full log method in logging helper.
Line(LoggingHelper.logNotWorking(LoggingHelper.java:12)) Message using class info method

When using this method, Log4j 2 will work as usual, avoiding calculating 1 the stack trace if it is not required.

Score: 5

Please note that giving the line number 6 is something very costly, either for what you get naturally 5 from Log4j or the following. You have to 4 accept that cost...

You could use the following 3 APIs:

    StackTraceElement[] stackTraces = Thread.currentThread().getStackTrace();
    StackTraceElement stackTraceElement = ...;
    stackTraceElement.getLineNumber();

Updated:

You would have to calculate 2 it yourself. So:

  • ask log4j not to output it (in your logging format),
  • and insert yourself the line number explicitement in the beginning of your message (the String you send to log4j).

Depending how you prefer 1 your loggers, your helper method may:

  • use an explicit Logger (passed as a parameter I guess), when appropriate (we sometimes define specific loggers for specific context ; for example, we have a logger for sending our database requests, no matter what class does it ; this allow us to reduce to one place the changes made to our configuration file, when we want to (de-)activate them ...)
  • use a Logger for the calling class : in this case, instead of passing the parameter, you can deduce the caller class name likewise...
Score: 4

Comes out that there is a very simple solution, just 3 add FQCN (The wrapper class' fully qualified 2 class name) to your logger helper:

public class MyLogger extends Logger {

private static final String FQCN = MyLogger.class.getName() + ".";

protected MyLogger(String name) {
    super(name);
}

public void info(final Object msg) {
    super.log(FQCN, Level.INFO, msg, null);
}

//etc...

In Your 1 working class you just do:

public class MyClass {

private static final Logger LOG = MyLogger.getLogger();   

private void test()
{
    LOG.info("test");
}

}
Score: 2

Adding details to KLE answer. (sorry, noob 11 user, don't know better way than creating 10 a separate answer )

Instead of sticking the 9 line number to the message, you can put 8 it in the MDC context. See org.apache.log4j.MDC

For 7 example:

StackTraceElement[] stackTraces = Thread.currentThread().getStackTrace();
StackTraceElement stackTraceElement = ...;
int l = stackTraceElement.getLineNumber();

MDC.put("myLineNumber", l);

That allows users to use mylineNumber 6 in their log4j configuration file

<layout class="org.apache.log4j.PatternLayout">
    <param name="ConversionPattern" 
           value="Line(%X{myLineNumber})- %m%n"/>
</layout>

Note: that 5 allows the user to control where and how 4 the line number appears in the message. However, since 3 getting the stacktrace is very costly, you 2 still need to find a way to switch off the 1 feature.

Score: 2

For Log4j2 the answer is provided completely 18 by the use of logger wrappers as described 17 in the Log4j2 manual under Example Usage of a Generated Logger Wrapper. One can simply 16 generate (using the org.apache.logging.log4j.core.tools.Generate$ExtendedLogger 15 tools illustrated there) a logger wrapper 14 with a single STUB level, and then adapt 13 that to create custom logging methods mimicking 12 the use of the logIfEnabled(FQCN, LEVEL, Marker, message, Throwable) - possibly 11 ignoring the STUB level and using the regular 10 ones - then if desired, deleting or commenting 9 out the STUB level and its methods). For 8 this purpose the FormattedMessage can be 7 helpful.

The source line, while expensive, can 6 then be easily shown as part of the full 5 location information by using the %l location 4 conversion pattern element in the PatternLayout given 3 in the configuration, or more specifically 2 using the %L line number and/or the %M method 1 conversion.

Now with complete example at: Java Logging: Log4j Version2.x: show the method of an end-client caller (not an intermediate logging helper method)

Score: 1

This isn't possible out of the box. The 4 best you can do in this case is to create 3 the logger in the caller and pass it to 2 the util method. This way, you can at least 1 get an idea where the call has come from.

Score: 1

If you have your own logging utility methods, you 6 could add linenumber and filename to the 5 logging argument list and take the cpp route. i.e. Preprocess 4 you source to replace tags like _ LINE _ and 3 _ FILE _ before you do the compile. As an added 2 bonus this would not take nerly as much 1 resources as figuring out at runtime.

Score: 0

Maybe you can implement the log helper function 8 using the stack trace element, get the line 7 numbers, and bypass the frames with method 6 with some specific annotations, like,

public @interface SkipFrame {}

// helper function
@SkipFrame // not necessary on the concrete log function
void log(String... message) {
    // getStackTrace()...
    int callerDepth = 2;  // a constant number depends on implementation
    StackTraceElement callerElement = null; 
    for (StackTraceElement e: stackTrace) {
         String className, methodName = e.getClassName, getMethodName()...
         Class callClass = Class.forName(className);
         // since there maybe several methods with the same name
         // here skip those overloaded methods
         Method callMethod = guessWhichMethodWithoutSignature(callClass, methodName);
         SkipFrame skipFrame = callMethod.getAnnotation(SkipFrame.class); 
         if (skipFrame != null)
             continue; // skip this stack trace element
         if (callerDepth-- == 0) {
             callerElement = e; 
             break;
         }
     }
     assert callerDepth == 0; 
     assert callerElement != null;
     Log4j.info(callerElement.getLineNumber()... + "message... "); 
}

@SkipFrame
void logSendMail(Mail mailObject) {
    log("Send mail " + mailObject.getSubject()); 
}

Thus, if 5 the helper function is nested, or there 4 are more utilized helper functions, just 3 mark the SkipFrame annotation on all of 2 them and you will get the correct source 1 line number what you really wanted.

More Related questions