OK,如今咱们来研究下common-logging的源码。这篇博客有参照上善若水的博客,感谢他的无私分享。java
先来随便扯点吧,貌似全部这些流行的Logging框架都和Log4J多少有点关系(不太肯定Commons Logging有多大关系,不过至少也都是Apache下的项目吧)。JDK Logging听说当初是想用Log4J的,可是当时两家好像谈判谈崩了,而后JDK本身实现了一个,貌似结构和Log4J差很少,只是实现的比较烂,基本上也只能在作测试的时候用,而SLF4J和LogBack都是出自Log4J的创始人Ceki Gülcü之手。这家伙也算是闲的蛋疼,光整Logging这些框架貌似就花了很多时间吧。
web
原本我要下载这个包的源码而后在本地跑一下的,结果一看jar包就这么几个类,因此呢?这里也就不直接跑源码和调试源码了。common-logging的包结构以下:apache
OK,言归正传,在Logging系统中,目前框架都是基于相同的设计,即从一个LogFactory中取得一个命名的Log(Logger)实例,而后使用这个Log(Logger)实例打印debug、info、warn、error等不一样级别的日志。做为两个门面日志系统,Commons Logging和SLF4J也一样采用这样的设计。所谓门面日志系统,是指它们自己并不实现具体的日志打印逻辑,它们只是做为一个代理系统,接收应用程序的日志打印请求,而后根据当前环境和配置,选取一个具体的日志实现系统,将真正的打印逻辑交给具体的日志实现系统,从而实现应用程序日志系统的“可插拔”,便可以经过配置或更换jar包来方便的更换底层日志实现系统,而不须要改变任何代码。我的感受SLF4J的实现更加灵活,而且它还提供了Maker和MDC的接口。关于SLF4J的整理我在后面会作具体介绍。设计模式
public static Log LOG = LogFactory.getLog(CommonsLoggingTest.class);
这里贴出LogFactory获取相对应的Log实现类的核心代码:数组
LogFactory抽象类:
缓存
public static Log getLog(Class clazz) throws LogConfigurationException { return getFactory().getInstance(clazz); }
LogFactoryImpl实现类:服务器
public Log getInstance(Class clazz) throws LogConfigurationException { return getInstance(clazz.getName()); }
public Log getInstance(String name) throws LogConfigurationException { Log instance = (Log) instances.get(name); if (instance == null) { instance = newInstance(name); instances.put(name, instance); } return instance; }
protected Log newInstance(String name) throws LogConfigurationException { Log instance; try { if (logConstructor == null) { instance = discoverLogImplementation(name); } else { Object params[] = { name }; instance = (Log) logConstructor.newInstance(params); } if (logMethod != null) { Object params[] = { this }; logMethod.invoke(instance, params); } return instance; }关于上面代码解释:
若是在获取具体的实例的时候,common-logging的logConstructor属性不为空,则直接抛下反射初始化该实例就OK,若是该属性为空,那么就要按照顺序去找对应的日志实例。核心代码以下:app
private Log discoverLogImplementation(String logCategory) throws LogConfigurationException { //1,初始化配置 initConfiguration(); //2,寻找用户自定义的日志实例 Log result = null; String specifiedLogClassName = findUserSpecifiedLogClassName(); //3,框架开始工做,按照顺序初始化log实例 if (specifiedLogClassName != null) { // 初始化log实例 result = createLogFromClass(specifiedLogClassName,logCategory,true); if (result == null) { StringBuffer messageBuffer = new StringBuffer("User-specified log class '"); messageBuffer.append(specifiedLogClassName); messageBuffer.append("' cannot be found or is not useable."); // 顺序链接报错字符串,抛出一个异常 informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_LOG4J_LOGGER); informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_JDK14_LOGGER); informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_LUMBERJACK_LOGGER); informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_SIMPLE_LOGGER); throw new LogConfigurationException(messageBuffer.toString()); } return result; }
下面先贴Log4JLogger核心源码:框架
public class Log4JLogger implements Log, Serializable { /** Serializable version identifier. */ private static final long serialVersionUID = 5160705895411730424L; /** The fully qualified name of the Log4JLogger class. */ private static final String FQCN = Log4JLogger.class.getName(); /** Log to this logger */ private transient volatile Logger logger = null; /** Logger name */ private final String name; private static final Priority traceLevel; static { if (!Priority.class.isAssignableFrom(Level.class)) { // nope, this is log4j 1.3, so force an ExceptionInInitializerError throw new InstantiationError("Log4J 1.2 not available"); } Priority _traceLevel; try { _traceLevel = (Priority) Level.class.getDeclaredField("TRACE").get(null); } catch(Exception ex) { // ok, trace not available _traceLevel = Level.DEBUG; } traceLevel = _traceLevel; } // ------------------------------------------------------------ Constructor public Log4JLogger() { name = null; } /** * Base constructor. */ public Log4JLogger(String name) { this.name = name; this.logger = getLogger(); } /** * For use with a log4j factory. */ public Log4JLogger(Logger logger) { if (logger == null) { throw new IllegalArgumentException( "Warning - null logger in constructor; possible log4j misconfiguration."); } this.name = logger.getName(); this.logger = logger; } /** * Logs a message with <code>org.apache.log4j.Priority.DEBUG</code>. * * @param message to log * @see org.apache.commons.logging.Log#debug(Object) */ public void debug(Object message) { getLogger().log(FQCN, Level.DEBUG, message, null); } /** * Logs a message with <code>org.apache.log4j.Priority.DEBUG</code>. * * @param message to log * @param t log this cause * @see org.apache.commons.logging.Log#debug(Object, Throwable) */ public void debug(Object message, Throwable t) { getLogger().log(FQCN, Level.DEBUG, message, t); } /** * Return the native Logger instance we are using. */ public Logger getLogger() { Logger result = logger; if (result == null) { synchronized(this) { result = logger; if (result == null) { logger = result = Logger.getLogger(name); } } } return result; } /** * Check whether the Log4j Logger used is enabled for <code>DEBUG</code> priority. */ public boolean isDebugEnabled() { return getLogger().isDebugEnabled(); } }
1,首先封装log4j的logger类做为属性,而后在封装一个name属性,该属性用来初始化logger时候的构造器参数,在初始化log4jLogger类的时候该name传入,而后用该name来初始化logger实例属性。webapp
2,对外提供一套和log4j一致的API,而后方法中调用logger属性相关API方法就OK了。这里没有直接用属性.方法(),而是用了getLogger(),这样子能够防止logger属性是null的状况,代码比较严谨。这点值得咱们学习。