恰好看到关于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()。
分享到:
相关推荐
例如,可以使用`Thread.currentThread().getStackTrace()`获取堆栈信息,然后找到日志方法被调用的位置,获取到正确的类名。 ```java public class MyLoggerFactory { public static Logger getLogger(Class<?> ...
通过`Thread.currentThread().getStackTrace()`,我们可以得到一个`StackTraceElement`数组,从中可以获取到调用栈的详细信息,包括类名、方法名和行号。 2. **控制日志输出**:LOG封装往往包含开关机制,允许...
`Log4Qt`允许用户自定义日志输出的格式,包括类名、函数名、线程ID、时间戳等信息。例如,通过设置布局器(`Layout`),可以在日志消息中包含这些元数据,提高日志的可读性和分析性。 ### 3. **日志输出目的地** `...
随后的`debug()`, `info()`和`warn()`方法调用会根据设置的级别记录相应级别的日志信息。 总结来说,Apache Log4j 是一个强大且可定制的日志框架,它通过Logger、Appender和Layout的组合,提供了灵活的日志记录功能...
注意:这里的`"com.example.MyJavaClass"`是你的Java类全限定名,确保替换为实际的包名和类名。 步骤4:处理权限和异常 在Unity中调用Java代码可能需要处理权限问题,以及可能出现的异常。确保在AndroidManifest....
要获取类名,我们需要访问`topActivity`属性,这是一个`ComponentName`对象,代表了Activity的包名和类名: ```java ComponentName component = cinfo.topActivity; ``` 最后,通过`ComponentName`的`getClassName...
在上述代码中,`logger`对象是通过`LoggerFactory.getLogger()`获取的,它会自动根据类名查找合适的日志实现,即Log4j。 4. **运行测试**:在`test-log4j`目录中的测试代码,应该是用来验证上述配置和日志记录功能...
然后,通过静态类`log4net.LogManager.GetLogger()`获取日志记录器,并调用其方法进行日志记录。例如: ```csharp using log4net; using log4net.Config; [assembly: log4net.Config.XmlConfigurator(Watch = true...
2. **类名和方法名自动添加**:为了便于追踪日志来源,`DebugLog`会在每条日志前自动添加当前调用者的类名和方法名,这样我们就能快速定位到出问题的代码行。 3. **堆栈信息**:`DebugLog`还可以选择性地输出调用...
StackTraceElement是Java中的一个类,它提供了获取方法调用栈信息的能力,这使得开发者可以轻松地获取当前方法的调用栈信息,从而方便地进行错误调试和日志记录。本文将详细介绍StackTraceElement的使用方法和实例...
使用`isDebugEnabled()`方法判断当前Logger是否开启DEBUG级别,如果开启了,则调用`debug()`方法记录日志信息。 #### 五、总结 通过以上步骤,我们可以清晰地了解到如何使用Log4j进行日志记录。从安装配置到具体...
这里,我们获取名为"MyApp"的logger实例,并根据传入的日志级别设置相应的Level对象,然后调用`forceLog`方法输出日志。 2. 使用宏:在代码中,你可以简单地调用LOG宏,如`LOG(DEBUG, "这是调试信息:%d", some...
在给定的“log4j的样例代码”中,我们可以深入理解Log4j的工作原理和配置方法。 **1. 日志级别** Log4j支持多种日志级别,包括TRACE, DEBUG, INFO, WARN, ERROR, FATAL和OFF。这些级别按照严重程度递增排序,开发...
2. **格式化输出**:可以定制LOG的输出格式,比如添加时间戳、线程信息、类名和方法名等,使得LOG更具可读性和可追溯性。 3. **过滤和筛选**:高级的LOG工具支持关键字过滤,用户可以根据需要查看特定模块或关键词...
3. **格式化日志输出**:为了提高可读性,可以设计一种日志格式,如包含时间戳、日志级别、类名、方法名和日志信息等。 4. **封装`Log`类的方法**:为每个`Log`类的静态方法创建对应的自定义方法,添加额外的类名和...
- 创建配置文件,包含要调用的类名和方法名。 - 使用`Properties`类读取配置文件。 - 在测试类中,根据配置文件中的内容动态创建对象并调用相应的方法。 - 示例配置文件格式: ```properties classname=...
它可以动态地获取类的信息,如类名、属性、方法、构造函数等,并在运行时创建对象、调用方法。反射的应用场景包括:动态加载和执行程序集、实现插件架构、序列化和反序列化等。 log4net作为日志框架,提供了多种...
- 通过`%property`占位符,可以插入运行时动态信息,如线程ID、类名等。 5. **源码分析** - `log4net.Repository.Hierarchy.Hierarchy`类管理Logger的层级结构和配置。 - `log4net.Appender.IAppender`接口定义...
`this.getClass().getSimpleName()`用于获取当前类的简单名称,即类名不包括包名的部分。 接下来,我们所有的活动都应继承自`BaseActivity`。例如,我们可以创建一个名为`MainActivity`的活动: ```java public ...
以上配置表示,日志信息将同时输出到控制台和名为`app.log`的文件中,且最低日志级别为DEBUG。 总结来说,Log4j-1.2.17提供了灵活的日志管理功能,通过配置文件可以定制化日志输出的方式和内容,对于开发、调试和...