• 得到当前堆栈信息的两种方式(Thread和Throwable)的纠结


          今天进行slf4j中logger的同步封装。主要目的是为了以后方便更换日志实现系统。

    遇到的问题:使用Thread.currentThread().getStackTrace()[1].getClassName()得到的是当前类而不是调用类。见以下代码:

    private org.slf4j.Logger logger = null;
    
    	/**
    	 * construction method
    	 */
    	public Logger(){
    		// get the current class logger
    		logger = LoggerFactory.getLogger(Thread.currentThread().getStackTrace()[1].getClassName());
    	}

    所以打印日志的时候并没有得到日志真正所属类的信息。


           然后,我进行了一组測试:

    測试A:

    public class ThreadTest {
    
    	public static void TestString(){
    		StackTraceElement[] arr = new Exception().getStackTrace();
    		for(int i=0;i<=arr.length-1;i++){
    			System.out.println(arr[i].getClassName()+";"+arr[i].getMethodName()+";"+arr[i].getFileName());
    		}
    	}
    }

    public class App 
    {
        public static void main( String[] args )
        {
            ThreadTest.TestString();
        }
    }

         结果是:

    test.ThreadTest;TestString;ThreadTest.java(当前方法和类)
    test.App;main;App.java(调用该方法的方法和类)

    測试B:

    public class ThreadTest {
    
    	public static void TestString(){
    		StackTraceElement[] arr = Thread.currentThread().getStackTrace();
    		for(int i=0;i<=arr.length-1;i++){
    			System.out.println(arr[i].getClassName()+";"+arr[i].getMethodName()+";"+arr[i].getFileName());
    		}
    	}
    }

    App类同上。得到的结果是:

    java.lang.Thread;getStackTrace;Thread.java(Thread的信息)
    test.ThreadTest;TestString;ThreadTest.java(当前方法和类)
    test.App;main;App.java(调用该方法的方法和类)

    啥米情况???难道就是由于Thread的getStackTrace比Throwable(Exception的父类)的getStackTrace多打了一段Thread类的信息???


            因为不确定是不是真的是这样子的,故去查看了下jdk的源代码(纯属装X,不抱幻想)。

    首先是Throwable的getStackTrace方法,代码例如以下:

     public StackTraceElement[] getStackTrace() {
            return getOurStackTrace().clone();
        }
    
        private synchronized StackTraceElement[] getOurStackTrace() {
            // Initialize stack trace field with information from
            // backtrace if this is the first call to this method
            if (stackTrace == UNASSIGNED_STACK ||
                (stackTrace == null && backtrace != null) /* Out of protocol state */) {
                int depth = getStackTraceDepth();
                stackTrace = new StackTraceElement[depth];
                for (int i=0; i < depth; i++)
                    stackTrace[i] = getStackTraceElement(i);
            } else if (stackTrace == null) {
                return UNASSIGNED_STACK;
            }
            return stackTrace;
        }

    貌似没有什么不正确劲的地方哈,当中

    int depth = getStackTraceDepth();
    stackTrace[i] = getStackTraceElement(i);
    两个方法都是native的,不予深究了。这里没有找到问题。就去看了下Thread的getStackTrace方法,代码例如以下:

    public StackTraceElement[] getStackTrace() {
            if (this != Thread.currentThread()) {
                // check for getStackTrace permission
                SecurityManager security = System.getSecurityManager();
                if (security != null) {
                    security.checkPermission(
                        SecurityConstants.GET_STACK_TRACE_PERMISSION);
                }
                // optimization so we do not call into the vm for threads that
                // have not yet started or have terminated
                if (!isAlive()) {
                    return EMPTY_STACK_TRACE;
                }
                StackTraceElement[][] stackTraceArray = dumpThreads(new Thread[] {this});
                StackTraceElement[] stackTrace = stackTraceArray[0];
                // a thread that was alive during the previous isAlive call may have
                // since terminated, therefore not having a stacktrace.
                if (stackTrace == null) {
                    stackTrace = EMPTY_STACK_TRACE;
                }
                return stackTrace;
            } else {
                // Don't need JVM help for current thread
                return (new Exception()).getStackTrace();
            }
        }

    乍一看也没啥错啊~~~不正确,突然间跟着逻辑走一下。在if语句里面由于

    this != Thread.currentThread()是为true的。所以方法会运行else里面的语句,里面是啥。!!


    return (new Exception()).getStackTrace();
    All right!

    就是这里,这里jvm去运行了new Exception().getStackTrace();就是这句话让使用Thread的getStackTrace方法就有可能多打印一句java.lang.Thread;getStackTrace;Thread.java出来。这也就是为什么使用

    logger = LoggerFactory.getLogger(Thread.currentThread().getStackTrace()[1].getClassName());
    会得不到正确的日志信息的原因了。

             应该就是这样子了。为了验证我想的对不正确,写了一个測试验证的样例,代码例如以下:

    public class TestException {
    
    	public static StackTraceElement[] getStackTrace() {
    		return new Exception().getStackTrace();
    	}
    }
    

    public class ThreadTest {
    
    	public static void TestString(){
    		StackTraceElement[] arr = TestException.getStackTrace();
    		for(int i=0;i<=arr.length-1;i++){
    			System.out.println(arr[i].getClassName()+";"+arr[i].getMethodName()+";"+arr[i].getFileName());
    		}
    	}
    }

    public class App 
    {
        public static void main( String[] args )
        {
            ThreadTest.TestString();
        }
    }

    运行之后的结果是:

    test.TestException;getStackTrace;TestException.java
    test.ThreadTest;TestString;ThreadTest.java
    test.App;main;App.java

    红色的一行即证明了我的想法是正确的!


            全部的验证至此结束了。这个问题最后解决的方法非常easy,要么使用new Exception().getStackTrace()[1];要么使用Thread.currentThread().getStackTrace()[2]。

    尽管这个问题非常小,也非常基础。可是我还是非常兴奋。毕竟粘上了源代码,顿时逼格升高不少~~~

            新手上路。高手饶命~!




  • 相关阅读:
    .Net Core使用Options模式来使用配置项
    git忽略已经提交的文件(git忽略文件不起作用)
    AirTest
    VSCode搭建rust开发环境
    动态编译和加载java代码
    JavaScript动态应用代码(有点像Java里的drools)
    Win10 Rust 编译报错: linking with `link.exe` failed: exit code: 1181
    git 拉取仓库的单个目录
    dart里实现类似Java里的--classpath的功能
    Rust离线安装
  • 原文地址:https://www.cnblogs.com/lcchuguo/p/5335689.html
Copyright © 2020-2023  润新知