今天进行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]。

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

新手上路。高手饶命~!

得到当前堆栈信息的两种方式(Thread和Throwable)的纠结的更多相关文章

  1. 26.OpenIdConnect获取用户信息的两种方式

    openId在OAuth基础之上,在下面这红框内拿到Authorization Code之后还可以返回IdToken. IdToken和AccessToken一起返回.IdToken就会包括了用户的信 ...

  2. 程序中使用log4J打印信息的两种方式

    (1)通过org.apache.commons.logging.Log 接口实例化: public static Log log = LogFactory.getLog(String name); p ...

  3. 在Scrapy中如何利用Xpath选择器从HTML中提取目标信息(两种方式)

    前一阵子我们介绍了如何启动Scrapy项目以及关于Scrapy爬虫的一些小技巧介绍,没来得及上车的小伙伴可以戳这些文章: 手把手教你如何新建scrapy爬虫框架的第一个项目(上) 手把手教你如何新建s ...

  4. 读取数据库配置信息的两种方式(以后开发项目用java链接数据库)-------java基础知识

    第一步:先建立jdbc.properties user=root password url/yanlong driver=com.mysql.jdbc.Driver 第一种方式:直接文件读取 pack ...

  5. 删除SVN版本信息的两种方式

    一.在linux下删除SVN版本信息 删除这些目录是很简单的,命令如下 find . -type d -name ".svn"|xargs rm -rf 或者 find . -ty ...

  6. django--通过jwt获取用户信息的两种方式

    HTTP请求是无状态的,我们通常会使用cookie或session对其进行状态保持,cookie存储在客户端,容易被用户误删,安全性不高,session存储在服务端,在服务器集群情况下需要解决sess ...

  7. linux内核分析作业4:使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用

    系统调用:库函数封装了系统调用,通过库函数和系统调用打交道 用户态:低级别执行状态,代码的掌控范围会受到限制. 内核态:高执行级别,代码可移植性特权指令,访问任意物理地址 为什么划分级别:如果全部特权 ...

  8. Struts2实现ajax的两种方式

    基于Struts2框架下实现Ajax有两种方式,第一种是原声的方式,另外一种是struts2自带的一个插件. js部分调用方式是一样的: JS代码: function testAjax() { var ...

  9. 在基于MVC的Web项目中使用Web API和直接连接两种方式混合式接入

    在我之前介绍的混合式开发框架中,其界面是基于Winform的实现方式,后台使用Web API.WCF服务以及直接连接数据库的几种方式混合式接入,在Web项目中我们也可以采用这种方式实现混合式的接入方式 ...

随机推荐

  1. Visual Studio 创建代码注释默认模版方法

    在日常的开发中我们经常需要为页面添加注释和版权等信息,这样我们就需要每次去拷贝粘贴同样的文字,为了减少这种重复性的工作,我们可以把这些信息保存在Visual Studio 2012类库模版文件里 1. ...

  2. oprofile使用方法

    安装oprofile,然后加载内核模块.#modprobe oprofile,模块加载后开始使用oprofile. 1. 首先设置监视内核,使用debuginfo提供的内核,/boot下面的内核无法使 ...

  3. AS 学习笔记 加载数据

    AS2 加载本地(外部)数据.swf .png .jpg 等资源使用loadMovie() 加载库里面的mc 用 attachMovie AS3 加载本地(外部)数据 用 Loader 类来完成这个操 ...

  4. 两种应该掌握的排序方法--------1.shell Sort

    先了解下什么都有什么排序算法 https://en.wikipedia.org/wiki/Sorting_algorithm http://zh.wikipedia.org/zh/%E6%8E%92% ...

  5. EventHandlerList z

    写一个类时,有时候会在同一个类上添加很多事件,事件很多的话,是不容易管理的,.NET提供的EventHandlerList可以辅助多个事件的管 理,但不方便的地方是,它不是类型安全的,缺少类型安全,多 ...

  6. 安装qc 出现error An error occurred while attempting to connect to the database.

    When trying to install mercury quality center starter edition 9.0 on Windows XP media center, I am g ...

  7. Java笔记(十九)……多线程

    概述 进程: 是一个正在执行中的程序 每一个进程执行都有一个执行顺序,该执行顺序是一个执行路径,或者叫一个控制单元 线程: 就是进程中的一个独立的控制单元,线程在控制着进程的执行 一个进程中至少有一个 ...

  8. bzoj 1922 [Sdoi2010]大陆争霸(最短路变形)

    Description 在一个遥远的世界里有两个国家:位于大陆西端的杰森国和位于大陆东端的 克里斯国.两个国家的人民分别信仰两个对立的神:杰森国信仰象征黑暗和毁灭 的神曾·布拉泽,而克里斯国信仰象征光 ...

  9. Storm系列(十)聚流示例

    功能:将多个数据源的数据汇集到一个处理单元进行集中分类处理: 入口类TestMain 1  ; i < size; i++) { 31              content += input ...

  10. HW5.10

    public class Solution { public static void main(String[] args) { int count = 0; for(int i = 1; i < ...