Java Logging: show the source line number of the caller (not the logging helper method)

前端 未结 8 1354
太阳男子
太阳男子 2020-12-08 03:50

The numerous (sigh...) logging frameworks for Java all do a nice job of showing the line number of the source file name for the method that created the log message:

相关标签:
8条回答
  • 2020-12-08 04:28

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

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

    For example:

    StackTraceElement[] stackTraces = Thread.currentThread().getStackTrace();
    StackTraceElement stackTraceElement = ...;
    int l = stackTraceElement.getLineNumber();
    
    MDC.put("myLineNumber", l);
    

    That allows users to use mylineNumber in their log4j configuration file

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

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

    0 讨论(0)
  • 2020-12-08 04:34

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

    You could use the following APIs:

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

    Updated:

    You would have to calculate 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 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...
    0 讨论(0)
  • 2020-12-08 04:36

    Comes out that there is a very simple solution, just add FQCN (The wrapper class' fully qualified 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 working class you just do:

    public class MyClass {
    
    private static final Logger LOG = MyLogger.getLogger();   
    
    private void test()
    {
        LOG.info("test");
    }
    
    }
    
    0 讨论(0)
  • 2020-12-08 04:38

    Maybe you can implement the log helper function using the stack trace element, get the line numbers, and bypass the frames with method 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 the helper function is nested, or there are more utilized helper functions, just mark the SkipFrame annotation on all of them and you will get the correct source line number what you really wanted.

    0 讨论(0)
  • 2020-12-08 04:39

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

    0 讨论(0)
  • 2020-12-08 04:43

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

    0 讨论(0)
提交回复
热议问题