首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 其他教程 > 开源软件 >

打包log工具类

2012-06-30 
封装log工具类本文是转载网上的一篇文章?对于开发和调试以及上线运营,日志必不可少,常用日志工具有:Log4j、

封装log工具类

本文是转载网上的一篇文章

?

对于开发和调试以及上线运营,日志必不可少,常用日志工具有:Log4j、common-logging、甚至System.out。
这里用Log4j为例,说说如何将日志输出和业务代码做到最大分离。(篇幅不长,最后是源代码)

Logger4j 一般使用方法:
1.定义配置文件:log4j.properties
2.在需要输出日志的类里获取Logger实例:Logger loger = Logger.getLogger(AAA.class);
3.输出日志:loger.debug("test");

大家这么做都已经轻车熟路了,但是这样做有两个问题:

(1)每个需要输出日志信息的类里,都要引入log4j的包,都要定义一个Logger实例。
(2)如果一旦系统由log4j改为别的日志输出方式,如System.out 或common-logging,则要修改N多个类的代码。

以上两点,使得业务代码和log4j的耦合太大。所以,如果我们能将日志输出定义为一个静态工具方法,改变这种情况。
定义一个静态工具类:

?

import org.apache.log4j.Logger ;public class Logs{private static Logger logger = Logger.getLogger(Logs.class);//debug方法,其他error、warn类似定义public static void debug(Object obj){    //如果更改日志出工工具,只需修改该行代码即可。如:可改为 System.out.println(obj);   logger.debug(obj);}}
?

?

这样我们就可以在其他类里用直接 Logs.debug("test"); 进行日志输出了。
而且,一旦我们改变了日志输出工具,只要修改 Logs类即可,其他业务类不必修改。

也许很多人也都能想到这么做,同时也有很多人会意识到这么做带来的另一个问题:源代码定位。
Log4j的日志信息,能够进行源代码定位——也就是说,能够在日志信息中输出,调用日志的类、方法及代码所在的行。
而当我们按上面方法改为静态输出后,就不能进行源代码定位了——虽然仍有源代码信息,但都是指向了Logs类。
这对程序员调试,以及跟踪、解决问题都不方便了。

不过既然Log4j能做到源代码定位,相信我们也会有办法做到,如果您有兴趣请继续看:

-------------------------------
Log4j能准确捕获源代码的所在的类、方法、行。但java并没有提供响应的方法,这似乎很神奇。
上网搜一下 “Log4j 行号” 已经有高人指出:Log4j是通过java错误堆栈来实现的,也就是说通过new一个异常Throwable,然后在捕获,从而得到堆栈信息,在进行分析就可以得到行号等了。
所以,有人提出象log4j那样,抛出一个异常,然后捕获分析,从而在我们自己的静态日志工具里实现源代码定位,但是这样就多抛出一次异常,效率肯定底了。
而且抛出异常过多,引起额外事故也是个重大问题。
不管怎么说,这毕竟是一个思路,我尝试着找其他能得到堆栈信息的方法,最后在Thread类里找到了一个getStackTrace();
这个方法是jdk1.5版本以后才有的,幸亏我用的是1.5。
我没有深入研究,但感觉用这个方法得到堆栈应该比抛出异常好多了。
于是我对静态日志工具类,进行了一番改造,自我感觉还是不错的,下面贴出源码,欢迎讨论。
说明:该类有main方法以供测试。

-----------------------------------------

?

/*** 日志输出代理类。* <p>* 主要完成业务代码和日志工具间的解耦,使切换日志工具更加方便。* <p>* 如:将Log4j改为common-logging或自己的独立实现,则只需调整该类即可。* <p>* 简化了调用方式:如使用log4j,通常用法是先生成一个实例,再调用输出方法。* 现在,只需直接使用 Logs.debug();等静态方法即可* * @author yangzi<p>*         2008-09-08* @version 1.0 * @since 1.5*/public class Logs {/*** 是否打印日志,true表示打印日志,false表示不打印。* <p>* 开发阶段可以将其设为ture,运行阶段可以设为false*/private static final boolean enabled = true;/** 是否进行源代码定位,ture表示输出源代码所在类以及代码行 */private static boolean showLocSrc = true;/** 指定的日志级别 */private static int level = 1;/** 日志级别:普通 */private static final int info = 1;/** 日志级别:调试 */private static final int debug = 2;/** 日志级别:警告 */private static final int warn = 3;/** 日志级别:错误 */private static final int error = 4;/** 消息所属和消息内容的分隔符 */private static final String msgSplit = ":";/** 该类的名称,用于识别该类的堆栈 */private static final String thisClassName = Logs.class.getName();/** 默认输出日志的日志工具:log4j */private static org.apache.log4j.Logger logger = null;public Logs() {}static {   // 可以单独指定log4j的配置文件,也可以使用默认。   // org.apache.log4j.PropertyConfigurator.configure("log4j.properties");   // 得到logger实例,作为输出工具。   // 此句作用是用一个输出实例,取代每个类里面的: Logger.getLogger(X.class)   logger = org.apache.log4j.Logger.getLogger("");}/*** 测试* * @param args*/public static void main(String args[]) {   Logs.debug("调试信息");}/*** 根据日志级别,输出日志。* <p>* 如果要改变日志输出工具,* <p>* 如:由原来的log4j改为System.out.println()或logging,则只需改动此类即可。* * @param level*            日志级别* @param message*            日志消息* @param ste*            堆栈信息。*            <p>*            如果不需要输出源代码信息,则只需将静态成员属性 showLocSrc设为false即可。*/private static void log(int level, Object message, StackTraceElement[] ste) {   if (ste != null) {    // 加入源代码定位    message = getStackMsg(ste) + msgSplit + message;   }   // 转入具体实现,此处为log4j,可以改为其他不同的日志实现。   switch (level) {   case info:    logger.info(message);    break;   case debug:    logger.debug(message);    break;   case warn:    logger.warn(message);    break;   case error:    logger.error(message);    break;   default:    logger.debug(message);   }}/*** 根据堆栈信息得到源代码行信息* <p>* 原理:本工具类的堆栈下一行即为源代码的最原始堆栈。* * @param ste*            堆栈信息* @return 调用输出日志的代码所在的类.方法.代码行的相关信息*         <p>*         如:com.MyClass 类里的 fun()方法调用了Logs.debug("test");*         <p>*         则堆栈信息为: com.MyClass.fun(MyClass.java 代码行号)*/private static String getStackMsg(StackTraceElement[] ste) {   if (ste == null)    return null;   boolean srcFlag = false;   for (int i = 0; i < ste.length; i++) {    StackTraceElement s = ste[i];    // 如果上一行堆栈代码是本类的堆栈,则该行代码则为源代码的最原始堆栈。    if (srcFlag) {     return s==null?"":s.toString();    }    // 定位本类的堆栈    if (thisClassName.equals(s.getClassName())) {     srcFlag = true;    }   }   return null;}/*** 输出info信息* * @param message*/public static void info(Object message) {   // 如果禁止日志或者输出级别不符,则返回。   if (!enabled || info < level)    return;   if (showLocSrc) {    log(info, message, Thread.currentThread().getStackTrace());   } else {    log(info, message, null);   }}/*** 输出debug信息* * @param message*/public static void debug(Object message) {   // 如果禁止日志或者输出级别不符,则返回。   if (!enabled || debug < level)    return;   if (showLocSrc) {    log(debug, message, Thread.currentThread().getStackTrace());   } else {    log(debug, message, null);   }}/*** 输出warn信息* * @param message*/public static void warn(Object message) {   // 如果禁止日志或者输出级别不符,则返回。   if (!enabled || warn < level)    return;   if (showLocSrc) {    log(warn, message, Thread.currentThread().getStackTrace());   } else {    log(warn, message, null);   }}/*** 输出error信息* * @param message*/public static void error(Object message) {   // 如果禁止日志或者输出级别不符,则返回。   if (!enabled || error < level)    return;   if (showLocSrc) {    log(error, message, Thread.currentThread().getStackTrace());   } else {    log(error, message, null);   }}}
?

热点排行