在以往的業務系統項目中,常常引入咱們想接入的日誌輸出POM依賴, 利用相應的Logger API 輸出日誌或想打印的信息。但在依賴Spring Framework,Dubbo 或者其餘項目時發現,只須要引入Logger相關Jar包依賴,就能夠自適配Log 輸出,利用適配後的日誌輸出系統打印相關信息。 它們的自適配是如何實現的呢?java
Dubbo日誌的調用方式,針對不一樣的日誌打印系統,採用統一的API調用及輸出,如:apache
/** * ChannelListenerDispatcher * * @author william.liangf */ public class ChannelHandlerDispatcher implements ChannelHandler { private static final Logger logger = LoggerFactory.getLogger(ChannelHandlerDispatcher.class); ........ }
LoggerFactory.getLogger 就能夠獲取這個類的統一的調用對象。設計模式
接下來查看日誌相關源碼位置app
Dubbo 日誌相關代碼在common.logger下,分模塊分包也是Dubbo模塊化分層的方式之一。框架
日誌系統UML圖以下所示:模塊化
Dubbo採用的日誌輸出方式是首先從dubbo.application.logger 系統變量中獲取屬性值,來判斷到底採用哪一種日誌輸出方式,若是沒設置則按照默認的加載順序加載相應的日誌輸出類,直到成功加載:spa
順序爲:log4jLogger > slf4jLogger > JclLogger > JdkLogger 。設計
接下來看LoggerFactory在類加載過程當中變量的初始化過程:日誌
static { String logger = System.getProperty("dubbo.application.logger"); if ("slf4j".equals(logger)) { setLoggerAdapter(new Slf4jLoggerAdapter()); } else if ("jcl".equals(logger)) { setLoggerAdapter(new JclLoggerAdapter()); } else if ("log4j".equals(logger)) { setLoggerAdapter(new Log4jLoggerAdapter()); } else if ("jdk".equals(logger)) { setLoggerAdapter(new JdkLoggerAdapter()); } else { try { setLoggerAdapter(new Log4jLoggerAdapter()); } catch (Throwable e1) { try { setLoggerAdapter(new Slf4jLoggerAdapter()); } catch (Throwable e2) { try { setLoggerAdapter(new JclLoggerAdapter()); } catch (Throwable e3) { setLoggerAdapter(new JdkLoggerAdapter()); } } } } }
能夠看出相關加載過程。code
LoggerFactory中有兩個靜態變量
private static final ConcurrentMap<String, FailsafeLogger> LOGGERS = new ConcurrentHashMap<String, FailsafeLogger>(); private static volatile LoggerAdapter LOGGER_ADAPTER;
LOGGER_ADAPTER 保存輸出方式對應的適配器對象。(切換日誌輸出時,保證內存的可見)
LOGGERS 存有不一樣Service業務類對象的Logger對象。 避免同一業務類的Logger對象頻繁建立問題。 (避免了業務人員對每次業務方法都經過LoggerFactory.getLogger 獲取Logger對象,致使的Logger對象的頻繁建立問題)
Dubbo經過這種方式在系統初始化(類加載)期間,完成了Logger的選型工做。
注: Logger 的選中根 dubbo.application.logger 系統變量設定 和 類加載順序相關