`
wtaoli
  • 浏览: 27791 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

Log信息获取调用类名和调用方法名的实现原理

阅读更多
恰好看到关于log的讨论。想起以前调查的一个问题。整理出来,希望对大家能有所帮助。

Sun JDK 源代码下载 http://wwws.sun.com/software/communitysource/
先注册并登录到“Sun Community Source Licensing”,然后下载J2SE(几十兆)或者J2EE(几百兆)。

Log能够把代码运行时间,类名,方法名,还有信息,全部都打印出来。
一个直观的例子,每次启动Tomcat(缺省配置)的时候。一般可以看到
Jul 9, 2004 11:22:29 AM org.apache.struts.util.PropertyMessageResources <init>
INFO: Initializing, config='org.apache.webapp.admin.ApplicationResources', returnNull=true
Jul 9, 2004 11:22:41 AM org.apache.coyote.http11.Http11Protocol start
INFO: Starting Coyote HTTP/1.1 on port 8080

时间,类名,方法名,信息都打印了出来。
那么,log是如何获取调用自身的这个类和这个方法名的呢?

后面给出的代码是JDK1.4的源代码,和Log4J的源代码。说明其实现原理。
获得调用类,和方法名,就是需要获得当前运行栈的结构。
new Throwable().getStackTrace() 会返回当前运行栈的结构层次。
利用这种原理,可以获得整个运行栈的调用关系。

JDK1.4的java.util.logging包, 通过Throwable.getStackTrace()方法实现的。
// Get the stack trace.
StackTraceElement stack[] = (new Throwable()).getStackTrace();

完整的代码在JDK1.4的源代码里面,java.util.logging.LogRecord类的inferCaller方法。
Java代码
// Private method to infer the caller's class and method names  
private void inferCaller() {  
needToInferCaller = false;  
// Get the stack trace.  
StackTraceElement stack[] = (new Throwable()).getStackTrace();  
// First, search back to a method in the Logger class.  
int ix = 0;  
while (ix < stack.length) {  
StackTraceElement frame = stack[ix];  
String cname = frame.getClassName();  
if (cname.equals("java.util.logging.Logger")) {  
break;  
}  
ix++;  
}  
// Now search for the first frame before the "Logger" class.  
while (ix < stack.length) {  
StackTraceElement frame = stack[ix];  
String cname = frame.getClassName();  
if (!cname.equals("java.util.logging.Logger")) {  
// We've found the relevant frame.  
setSourceClassName(cname);  
setSourceMethodName(frame.getMethodName());  
return;  
}  
ix++;  
}  
// We haven't found a suitable frame, so just punt. This is  
// OK as we are only commited to making a "best effort" here.  


// Private method to infer the caller's class and method names
private void inferCaller() {
needToInferCaller = false;
// Get the stack trace.
StackTraceElement stack[] = (new Throwable()).getStackTrace();
// First, search back to a method in the Logger class.
int ix = 0;
while (ix < stack.length) {
StackTraceElement frame = stack[ix];
String cname = frame.getClassName();
if (cname.equals("java.util.logging.Logger")) {
break;
}
ix++;
}
// Now search for the first frame before the "Logger" class.
while (ix < stack.length) {
StackTraceElement frame = stack[ix];
String cname = frame.getClassName();
if (!cname.equals("java.util.logging.Logger")) {
// We've found the relevant frame.
setSourceClassName(cname);
setSourceMethodName(frame.getMethodName());
return;
}
ix++;
}
// We haven't found a suitable frame, so just punt. This is
// OK as we are only commited to making a "best effort" here.
}


Log4j有异曲同工之妙。
org.apache.log4j.spi.LocationInfo类。
先用Throwable.printStackTrace()方法把Exception信息打印到一个字符串里。
然后按行分析这个字符串。抽出调用类和方法。参见LocationInfo类的构造函数。

Java代码
/** 
Instantiate location information based on a Throwable. We 
expect the Throwable <code>t</code>, to be in the format 

<pre> 
java.lang.Throwable 
... 
at org.apache.log4j.PatternLayout.format(PatternLayout.java:413) 
at org.apache.log4j.FileAppender.doAppend(FileAppender.java:183) 
at org.apache.log4j.Category.callAppenders(Category.java:131) 
at org.apache.log4j.Category.log(Category.java:512) 
at callers.fully.qualified.className.methodName(FileName.java:74) 
... 
</pre> 

<p>However, we can also deal with JIT compilers that "lose" the 
location information, especially between the parentheses. 

*/ 
public LocationInfo(Throwable t, String fqnOfCallingClass) 

/**
Instantiate location information based on a Throwable. We
expect the Throwable <code>t</code>, to be in the format

<pre>
java.lang.Throwable
...
at org.apache.log4j.PatternLayout.format(PatternLayout.java:413)
at org.apache.log4j.FileAppender.doAppend(FileAppender.java:183)
at org.apache.log4j.Category.callAppenders(Category.java:131)
at org.apache.log4j.Category.log(Category.java:512)
at callers.fully.qualified.className.methodName(FileName.java:74)
...
</pre>

<p>However, we can also deal with JIT compilers that "lose" the
location information, especially between the parentheses.

*/
public LocationInfo(Throwable t, String fqnOfCallingClass)


e.printStackTrace()把Exception发生当时的整个运行栈结构展开,打印出来。
Log4J就是分析这个打印结果,获得所有的调用层次。

关于直接获取调用类名的方法。
我们来看sun.reflect.Reflection的getCallerClass()方法的说明。

Java代码
/** Returns the class of the method <code>realFramesToSkip</code> 
frames up the stack (zero-based), ignoring frames associated 
with java.lang.reflect.Method.invoke() and its implementation. 
The first frame is that associated with this method, so 
<code>getCallerClass(0)</code> returns the Class object for 
sun.reflect.Reflection. Frames associated with 
java.lang.reflect.Method.invoke() and its implementation are 
completely ignored and do not count toward the number of "real" 
frames skipped. */ 
public static native Class getCallerClass(int realFramesToSkip); 

/** Returns the class of the method <code>realFramesToSkip</code>
frames up the stack (zero-based), ignoring frames associated
with java.lang.reflect.Method.invoke() and its implementation.
The first frame is that associated with this method, so
<code>getCallerClass(0)</code> returns the Class object for
sun.reflect.Reflection. Frames associated with
java.lang.reflect.Method.invoke() and its implementation are
completely ignored and do not count toward the number of "real"
frames skipped. */
public static native Class getCallerClass(int realFramesToSkip);


这是一个native方法。原理也是根据StackFrame(运行栈)获取相应类的信息。这个方法直接返回一个Class名字,直接有效。参数realFramesToSkip用来选取你所需要的Stack层次,所以,你可以用这个方法获得任何层次的上的调用类名。

Throwable.getStackTrace()也是一个native方法。原理也是根据StackFrame(运行栈)获取相应类的信息。返回一个StackTraceElement[]。
StackTraceElement类在JDK1.4的java.lang的包里面。里面包含丰富的信息,非常适合Debug。
StackTraceElement类有如下方法:
getFileName(),getLineNumber(), getClassName(), getMethodName()。
分享到:
评论

相关推荐

    间接调用Log4j的日志功能导致类名输出错误解决方案

    例如,可以使用`Thread.currentThread().getStackTrace()`获取堆栈信息,然后找到日志方法被调用的位置,获取到正确的类名。 ```java public class MyLoggerFactory { public static Logger getLogger(Class&lt;?&gt; ...

    很好用的LOG封装,可同时输出类名,方法名,行数,可控制输出不输出

    通过`Thread.currentThread().getStackTrace()`,我们可以得到一个`StackTraceElement`数组,从中可以获取到调用栈的详细信息,包括类名、方法名和行号。 2. **控制日志输出**:LOG封装往往包含开关机制,允许...

    log4Qt 支持函数名,类名

    `Log4Qt`允许用户自定义日志输出的格式,包括类名、函数名、线程ID、时间戳等信息。例如,通过设置布局器(`Layout`),可以在日志消息中包含这些元数据,提高日志的可读性和分析性。 ### 3. **日志输出目的地** `...

    apache log4j 日志系统实现原理.doc

    随后的`debug()`, `info()`和`warn()`方法调用会根据设置的级别记录相应级别的日志信息。 总结来说,Apache Log4j 是一个强大且可定制的日志框架,它通过Logger、Appender和Layout的组合,提供了灵活的日志记录功能...

    unity3d调用jar方法和属性的例子

    注意:这里的`"com.example.MyJavaClass"`是你的Java类全限定名,确保替换为实际的包名和类名。 步骤4:处理权限和异常 在Unity中调用Java代码可能需要处理权限问题,以及可能出现的异常。确保在AndroidManifest....

    Android获得当前正在显示的activity类名的方法

    要获取类名,我们需要访问`topActivity`属性,这是一个`ComponentName`对象,代表了Activity的包名和类名: ```java ComponentName component = cinfo.topActivity; ``` 最后,通过`ComponentName`的`getClassName...

    log4j+slf4j实现 log4j测试代码,log4j+slf4j实现 log4j测试代码

    在上述代码中,`logger`对象是通过`LoggerFactory.getLogger()`获取的,它会自动根据类名查找合适的日志实现,即Log4j。 4. **运行测试**:在`test-log4j`目录中的测试代码,应该是用来验证上述配置和日志记录功能...

    log4Net.dll+使用详解

    然后,通过静态类`log4net.LogManager.GetLogger()`获取日志记录器,并调用其方法进行日志记录。例如: ```csharp using log4net; using log4net.Config; [assembly: log4net.Config.XmlConfigurator(Watch = true...

    DebugLog扩展调试demo

    2. **类名和方法名自动添加**:为了便于追踪日志来源,`DebugLog`会在每条日志前自动添加当前调用者的类名和方法名,这样我们就能快速定位到出问题的代码行。 3. **堆栈信息**:`DebugLog`还可以选择性地输出调用...

    StackTraceElement获取方法调用栈信息实例详解

    StackTraceElement是Java中的一个类,它提供了获取方法调用栈信息的能力,这使得开发者可以轻松地获取当前方法的调用栈信息,从而方便地进行错误调试和日志记录。本文将详细介绍StackTraceElement的使用方法和实例...

    如何使用Log4j如何使用Log4j

    使用`isDebugEnabled()`方法判断当前Logger是否开启DEBUG级别,如果开启了,则调用`debug()`方法记录日志信息。 #### 五、总结 通过以上步骤,我们可以清晰地了解到如何使用Log4j进行日志记录。从安装配置到具体...

    log4cxx封装为通用LOG宏

    这里,我们获取名为"MyApp"的logger实例,并根据传入的日志级别设置相应的Level对象,然后调用`forceLog`方法输出日志。 2. 使用宏:在代码中,你可以简单地调用LOG宏,如`LOG(DEBUG, "这是调试信息:%d", some...

    log4j的样例代码

    在给定的“log4j的样例代码”中,我们可以深入理解Log4j的工作原理和配置方法。 **1. 日志级别** Log4j支持多种日志级别,包括TRACE, DEBUG, INFO, WARN, ERROR, FATAL和OFF。这些级别按照严重程度递增排序,开发...

    android打印log工具

    2. **格式化输出**:可以定制LOG的输出格式,比如添加时间戳、线程信息、类名和方法名等,使得LOG更具可读性和可追溯性。 3. **过滤和筛选**:高级的LOG工具支持关键字过滤,用户可以根据需要查看特定模块或关键词...

    android日志打印工具类

    3. **格式化日志输出**:为了提高可读性,可以设计一种日志格式,如包含时间戳、日志级别、类名、方法名和日志信息等。 4. **封装`Log`类的方法**:为每个`Log`类的静态方法创建对应的自定义方法,添加额外的类名和...

    大数据处理方法和技术实验一-RPC和反射机制应用.docx

    - 创建配置文件,包含要调用的类名和方法名。 - 使用`Properties`类读取配置文件。 - 在测试类中,根据配置文件中的内容动态创建对象并调用相应的方法。 - 示例配置文件格式: ```properties classname=...

    设计模式 工厂 反射 log4net

    它可以动态地获取类的信息,如类名、属性、方法、构造函数等,并在运行时创建对象、调用方法。反射的应用场景包括:动态加载和执行程序集、实现插件架构、序列化和反序列化等。 log4net作为日志框架,提供了多种...

    log4net-2.0.8-src_log4net源代码_log4net_

    - 通过`%property`占位符,可以插入运行时动态信息,如线程ID、类名等。 5. **源码分析** - `log4net.Repository.Hierarchy.Hierarchy`类管理Logger的层级结构和配置。 - `log4net.Appender.IAppender`接口定义...

    活动管理器,类名辨识

    `this.getClass().getSimpleName()`用于获取当前类的简单名称,即类名不包括包名的部分。 接下来,我们所有的活动都应继承自`BaseActivity`。例如,我们可以创建一个名为`MainActivity`的活动: ```java public ...

    log4j-1.2.17的jar包以及依赖包,还有一份log4j的配置文件,输出到控制台和文件夹两种配置

    以上配置表示,日志信息将同时输出到控制台和名为`app.log`的文件中,且最低日志级别为DEBUG。 总结来说,Log4j-1.2.17提供了灵活的日志管理功能,通过配置文件可以定制化日志输出的方式和内容,对于开发、调试和...

Global site tag (gtag.js) - Google Analytics