得到当前堆栈信息的两种方式(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]。
尽管这个问题非常小,也非常基础。可是我还是非常兴奋。毕竟粘上了源代码,顿时逼格升高不少~~~
新手上路。高手饶命~!
得到当前堆栈信息的两种方式(Thread和Throwable)的纠结的更多相关文章
- 26.OpenIdConnect获取用户信息的两种方式
openId在OAuth基础之上,在下面这红框内拿到Authorization Code之后还可以返回IdToken. IdToken和AccessToken一起返回.IdToken就会包括了用户的信 ...
- 程序中使用log4J打印信息的两种方式
(1)通过org.apache.commons.logging.Log 接口实例化: public static Log log = LogFactory.getLog(String name); p ...
- 在Scrapy中如何利用Xpath选择器从HTML中提取目标信息(两种方式)
前一阵子我们介绍了如何启动Scrapy项目以及关于Scrapy爬虫的一些小技巧介绍,没来得及上车的小伙伴可以戳这些文章: 手把手教你如何新建scrapy爬虫框架的第一个项目(上) 手把手教你如何新建s ...
- 读取数据库配置信息的两种方式(以后开发项目用java链接数据库)-------java基础知识
第一步:先建立jdbc.properties user=root password url/yanlong driver=com.mysql.jdbc.Driver 第一种方式:直接文件读取 pack ...
- 删除SVN版本信息的两种方式
一.在linux下删除SVN版本信息 删除这些目录是很简单的,命令如下 find . -type d -name ".svn"|xargs rm -rf 或者 find . -ty ...
- django--通过jwt获取用户信息的两种方式
HTTP请求是无状态的,我们通常会使用cookie或session对其进行状态保持,cookie存储在客户端,容易被用户误删,安全性不高,session存储在服务端,在服务器集群情况下需要解决sess ...
- linux内核分析作业4:使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用
系统调用:库函数封装了系统调用,通过库函数和系统调用打交道 用户态:低级别执行状态,代码的掌控范围会受到限制. 内核态:高执行级别,代码可移植性特权指令,访问任意物理地址 为什么划分级别:如果全部特权 ...
- Struts2实现ajax的两种方式
基于Struts2框架下实现Ajax有两种方式,第一种是原声的方式,另外一种是struts2自带的一个插件. js部分调用方式是一样的: JS代码: function testAjax() { var ...
- 在基于MVC的Web项目中使用Web API和直接连接两种方式混合式接入
在我之前介绍的混合式开发框架中,其界面是基于Winform的实现方式,后台使用Web API.WCF服务以及直接连接数据库的几种方式混合式接入,在Web项目中我们也可以采用这种方式实现混合式的接入方式 ...
随机推荐
- [jobdu]二维数组中的查找
http://ac.jobdu.com/problem.php?pid=1384 基本思路很简单,从最右上角找起. 九度的OJ做得还是不太行啊.必须要int main()才行,这道题时间卡得太紧,用c ...
- Ubuntu10.10 安装scim
Ubuntu10.10 上没有找到默认的输入法,所以要安装一个中文输入法,网上好多介绍的,但都 不怎么好用,下面参考http://blog.csdn.net/caodesheng110/article ...
- Android 自定义title样式
requestWindowFeature(featrueId),它的功能是启用窗体的扩展特性.参数是Window类中定义的常量.一.枚举常量1.DEFAULT_FEATURES:系统默认状态,一般不需 ...
- Android-AttributeSet详解
public interface AttributeSet { /** * Returns the number of attributes available in the set. * * @re ...
- oracle database resident connection pooling(驻留连接池)
oracle在11g中引入了database resident connection pooling(DRCP).在此之前,我们可以使用dedicated 或者share 方式来链接数据库,dedic ...
- linux中fork()函数详解(原创!!实例讲解)
一.fork入门知识 一个进程,包括代码.数据和分配给进程的资源.fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同, ...
- 【原创】FPGA开发手记(二) VGA接口
以下内容均以Xilinx的Nexys3作为开发板 1.VGA接口介绍 首先,先看电路图(3*5为例): 标准VGA一共15个接口,但是实际应用的接口信号只用五个:HSYNC,行同步信号:VSYNC,场 ...
- C#中的异常处理
C#中的异常处理 while (ex != null) { WriteExceptionLog(ex, fileName); ex = ex.InnerException; } /// <sum ...
- [Hadoop源码解读](三)MapReduce篇之Job类
下面,我们只涉及MapReduce 1,而不涉及YARN. 当我们在写MapReduce程序的时候,通常,在main函数里,我们会像下面这样做.建立一个Job对象,设置它的JobName,然后配置输入 ...
- Mysql规范和使用注意点(转)
命名规范: 1表名,字段名,索引名称使用小写字母,数字采用下划线进行分割 2.表名采用模块名3个缩小字符 '前缀'之后顺序为表明 3.表名,字段名不超过32个字符 4.存储尸体数据的表,名称使用名词, ...