在开源的项目中使用 Log4j一般 都是在类中添加一静态变量,如
protected static Log log = LogFactory.getLog(RequestProcessor.class); //通用日志组件
或
protected static Logger log = Logger.getLogger(RequestProcessor.class); //直接申明为Log4j的logger
原来有一个项目是做了一个自定义了 MyLogger 类, 其中的 debug, info 等到方法直接就是调用 log4j 的 logger 的对应方法. 别的代码中调用 MyLogger 的静态方法打印日志时, 依据log4j.properties的配置显示 %l 定位信息就始终是 MyLogger
如: 2007-05-23 12:18:46,828 [DEBUG] com.unmi.MyLogger.debug(MyLogger.java:12) Hello MyLogger
而不是调用类,
如:2007-05-23 12:18:46,828 [DEBUG] com.unmi.MyClass.debug(MyClass.java:7) Hello MyLogger
记得当时为了解决这个问题, 是在 MyLogger 的日志打印方法中 new Throw() 实例,然后从异常栈中找到调用类的信息,放在msg中输出,而不用 %l 输出,后来发现Log4j的代码行定位的实现也如出一则。前面那个项目的做法也就造成了两次找寻异常栈信息,势必耗费不少的资源。
于是思量着,是否能在 MyLogger 中不 new Throw() 也能让 log4j 有正确的定位信息输出, 考虑直接用 AspectJ 来拦截,同时带着一个疑问:是不是用 Logger.getLogger(Class clz) 构造 Logger 时传入的是什么Class实例,输出时就会定位在这个类上。对此作了下面的试验,共有四个文件,Eclipse 中安装了 AspectJ 的插件 ajdt
一:MyLogger.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
package com.unmi; import org.apache.log4j.Logger; public class MyLogger { //留待Aspecj来初始化 public static Logger log; //打印日志的方法 public static void debug(String msg) { log.debug(msg); } } |
二:MyClass.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
package com.unmi; public class MyClass { public void foo() { //希望打印日志定位是在这一行 MyLogger.debug("Hello MyLogger"); } public static final void main(String args[]) { MyClass myObject = new MyClass(); myObject.foo(); } } |
三:LoggerRecipe.aj
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
package com.unmi; import org.apache.log4j.Logger; public aspect LoggerRecipe { //拦截MyLogger中的所有日志打印方法 pointcut logMethod():call(public void com.unmi.MyLogger.*(..)); void around():logMethod() { //能获取到需打印日志的类实例 Class clz = thisJoinPoint.getThis().getClass(); System.out.println("Construct Logger by Class: "+clz); //在执行日志方法前初始化Logger实例 MyLogger.log = Logger.getLogger(clz); //执行实际的日志打印方法 proceed(); } } |
四:log4j.properties
1 2 3 4 |
log4j.rootLogger=DEBUG,console log4j.appender.console=org.apache.log4j.ConsoleAppender log4j.appender.console.layout=org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} [%5p] %l %m%n |
执行 MyClass 后的输出是:
Construct Logger by Class: class com.unmi.MyClass
2007-05-23 12:29:29,031 [DEBUG] com.unmi.MyLogger.debug(MyLogger.java:12) Hello MyLogger
我们能看到确实在用调用类 MyClass 构造的 Logger 实例,但是输出日志时指示仍然是 MyLogger, 必须修改 Log4j 的源码才能输出日志时定位在 com.unmi.MyClass中
关于 Log4j 如何获知调用类的信息请参看以前写的一篇日志:Log4j是输出日志时是如何获知当前方法、行号的
其实这样的想法与做法即使达成所愿也不会带多大益处,因为并没有减少代码的侵入性,仍然要引用自定义日志类,写下日志输出方法。 Logger.getLogger(Class clz)传入的 Class 只不过是一个 Logger 实例缓存时的标识。同时关于这个实现的思考也可以停下来了。
Spring似乎也没有完美的解决此种问题,当你把一个 Advice 中的 before 方法写成
before(Method method, Object[] args, Object target) throws Throwabel
{
Logger log = Logger.getLogger(target.getClass());
log.debug(method.getName());
}
输出的日志也是定位在你的 Advice 类中,而不是你所拦截的那个类。
本文链接 https://yanbin.blog/log4j-static-method-log/, 来自 隔叶黄莺 Yanbin Blog
[版权声明] 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。