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的狀況,代碼比較嚴謹。這點值得咱們學習。