概述

打印日志是基本的调试步骤。一般我们会打印出当前的类全限定名.方法名后面跟上日志内容。通过这种方式来定位日志打印的位置。从服务器上或文件上只能这样。不过如果是本地调试,比如在IDE里运行项目时,我们希望有种更快捷的方式啦定位代码,就像异常信息的代码链接一样。此方法适用于Eclipse、Idea等一切支持异常输出的IDE。

代码实践

以下代码是最核心逻辑,可自行扩展更多内容
另外行号、线程安全根据需要自选。

/**
 * Created by 道长 on 2017/8/14 14:36
 * 日志记录工具,利用线程调用链打印代码链接。
 * 在控制台点击可以直接跳转日志输出位置,方便调试
 * 线程安全、序号可选
 */
public class LogUtil {
    private static boolean D = true;
    volatile static int n=0;

    public synchronized static void log(String msg) {
        if(D){
            System.err.println("["+(++n)+"] "+msg+"--"+Thread.currentThread().getStackTrace()[2]);
        }
    }
}

//测试
@Test
public void test1(){
    LogUtil.log("点我就知道消息从哪里来");
}
//输出结果
[1] 点我就知道消息从哪里来--LogTest.test1(LogTest.java:15)
[2] 点我就知道消息从哪里来--LogTest.test1(LogTest.java:15)
[3] 点我就知道消息从哪里来--LogTest.test1(LogTest.java:15)
[4] 点我就知道消息从哪里来--LogTest.test1(LogTest.java:15)

设计原理

Exception("我是异常").printStackTrace()会在控制台打印当前线程的调用栈。跟踪printStackTrace()代码,最后其通过循环打印StackTraceElement完成异常输出:

StackTraceElement[] trace = getOurStackTrace();
 for (StackTraceElement traceElement : trace){
     println("\tat " + traceElement);
 }

看下StackTraceElement是什么:

public final class StackTraceElement implements java.io.Serializable {
    // Normally initialized by VM (public constructor added in 1.5)
    private String declaringClass;
    private String methodName;
    private String fileName;
    private int    lineNumber;

里面描述了当前的类、文件名、代码行号等信息。通过这些信息足够定位到源代码。
Thread.currentThread().getStackTrace()返回的调用栈,下标2所指对象即是日志位置。

设计扩展

  • 行号设计:读开源代码时,我们会把工程导入IDE运行,跟踪代码。不过较大的工程复杂度高,代码运行顺序短时间内很难理清。利用自增行号,我们只需要在希望输出日志的地方调用LogUtil.log(msg),控制台上就能自动分配行号从而让你更快速了解当前代码在整个工程中的调用位置
  • 线程安全:保证行号有序
  • 可以参考日志框架,设计不同级别实现更细粒度控制。比如info,debug 等。