源碼基於logback 1.1.7java
首先看個demo:node
根據類名獲取Logger,這裏是用slf4j提供的工廠來獲取logger的:api
private static final Logger logger = LoggerFactory.getLogger("className");
進入getLogger方法:併發
public static Logger getLogger(String name) { ILoggerFactory iLoggerFactory = getILoggerFactory(); return iLoggerFactory.getLogger(name); }
首先獲取ILoggerFactory,而後根據name獲取Logger。app
getILoggerFactory方法:ui
public static ILoggerFactory getILoggerFactory() { if (INITIALIZATION_STATE == UNINITIALIZED) { INITIALIZATION_STATE = ONGOING_INITIALIZATION; //執行初始化 performInitialization(); } switch (INITIALIZATION_STATE) { //初始化成功則調用logback的StaticLoggerBinder返回LoggerFactory case SUCCESSFUL_INITIALIZATION: return StaticLoggerBinder.getSingleton().getLoggerFactory(); //沒有找到StaticLoggerBinder這個類 case NOP_FALLBACK_INITIALIZATION: return NOP_FALLBACK_FACTORY; //版本不對,或者StaticLoggerBinder包路徑不對,或者其餘 case FAILED_INITIALIZATION: throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG); //線程A已經快執行完performInitialization的時候,INITIALIZATION_STATE設置成SUCCESSFUL_INITIALIZATION, //線程B剛開始調用performInitialization,又將INITIALIZATION_STATE修改爲ONGOING_INITIALIZATION //線程A執行到switch代碼,就會跳轉到這裏,返回TEMP_FACTORY //TEMP_FACTORY如其名,調用它的getLogger方法獲取到SubstituteLogger。 //使用SubstituteLogger的info、error等方法打印日誌的時候會判斷它的_delegate屬性是否爲空 //一開始_delegate爲空,返回一個臨時的NOPLogger.NOP_LOGGER實例,該實例什麼也不作 //後期performInitialization方法中會調用fixSubstitutedLoggers方法,從新設置_delegate爲正確的logger。 case ONGOING_INITIALIZATION: // support re-entrant behavior. // See also http://bugzilla.slf4j.org/show_bug.cgi?id=106 return TEMP_FACTORY; } throw new IllegalStateException("Unreachable code"); }
下面看看執行初始化的方法 performInitialization:this
private final static void performInitialization() { bind(); if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) { //版本檢查 versionSanityCheck(); } } private final static void bind() { try { //查找org/slf4j/impl/StaticLoggerBinder.class,可能存在多個 Set<URL> staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet(); reportMultipleBindingAmbiguity(staticLoggerBinderPathSet); // the next line does the binding //若是沒有StaticLoggerBinder class這裏調用便會異常 StaticLoggerBinder.getSingleton(); //設置狀態爲成功初始化 INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION; //若是StaticLoggerBinder class個數大於1個,則進行上報實際綁定的類型 reportActualBinding(staticLoggerBinderPathSet); //修復併發調用getILoggerFactory時出現狀態爲ONGOING_INITIALIZATION且獲取到的logger爲TEMP_FACTORY的問題 //解決方法是設置每一個SubstituteLogger的_delegate爲正確的logger fixSubstitutedLoggers(); } catch (NoClassDefFoundError ncde) { String msg = ncde.getMessage(); if (messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) { INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION; Util.report("Failed to load class \"org.slf4j.impl.StaticLoggerBinder\"."); Util.report("Defaulting to no-operation (NOP) logger implementation"); Util.report("See " + NO_STATICLOGGERBINDER_URL + " for further details."); } else { failedBinding(ncde); throw ncde; } } catch (java.lang.NoSuchMethodError nsme) { String msg = nsme.getMessage(); if (msg != null && msg.indexOf("org.slf4j.impl.StaticLoggerBinder.getSingleton()") != -1) { INITIALIZATION_STATE = FAILED_INITIALIZATION; Util.report("slf4j-api 1.6.x (or later) is incompatible with this binding."); Util.report("Your binding is version 1.5.5 or earlier."); Util.report("Upgrade your binding to version 1.6.x."); } throw nsme; } catch (Exception e) { failedBinding(e); throw new IllegalStateException("Unexpected initialization failure", e); } }
執行完performInitialization方法後,狀態被設置成SUCCESSFUL_INITIALIZATION,而後執行StaticLoggerBinder.getSingleton().getLoggerFactory()。url
在這以前先看看StaticLoggerBinder的靜態代碼塊:spa
private static StaticLoggerBinder SINGLETON = new StaticLoggerBinder(); private static Object KEY = new Object(); static { //StaticLoggerBinder單例調用init方法 SINGLETON.init(); } private boolean initialized = false; //生成默認的LoggerContext private LoggerContext defaultLoggerContext = new LoggerContext(); private final ContextSelectorStaticBinder contextSelectorBinder = ContextSelectorStaticBinder.getSingleton(); private StaticLoggerBinder() { defaultLoggerContext.setName(CoreConstants.DEFAULT_CONTEXT_NAME); } public static StaticLoggerBinder getSingleton() { return SINGLETON; }
void init() { try { try { //建立ContextInitializer,調用autoConfig解析配置文件 new ContextInitializer(defaultLoggerContext).autoConfig(); } catch (JoranException je) { Util.report("Failed to auto configure default logger context", je); } // logback-292 //打印logback內部error、warn信息 //若是沒有配置StatusListener,則使用StatusPrinter的stdout方式打印 //配置StatusListener的代碼實如今ContextInitializer#autoConfig()方法第一行 //內部產生的error、warn等日誌在StaticLoggerBinder.defaultLoggerContext.sm中維護的。 //好比以前autoConfig方法中解析xml配置時出現error或者warn日誌會在sm中存儲,而後在此處打印 if (!StatusUtil.contextHasStatusListener(defaultLoggerContext)) { StatusPrinter.printInCaseOfErrorsOrWarnings(defaultLoggerContext); } contextSelectorBinder.init(defaultLoggerContext, KEY); //initialized初始爲false,init成功設置成true initialized = true; } catch (Throwable t) { // we should never get here Util.report("Failed to instantiate [" + LoggerContext.class.getName() + "]", t); } }
接下來分析ContextInitializer.autoConfig:線程
public void autoConfig() throws JoranException { //解析在環境變量中配置的StatusListener實現類,若是配置的話生成相應的實例, //調用其start方法,上報過去一段時間logback內部日誌 StatusListenerConfigHelper.installIfAsked(loggerContext); //獲取logback配置文件URL URL url = findURLOfDefaultConfigurationFile(true); if (url != null) { //解析logback配置文件,核心 configureByResource(url); } else { Configurator c = EnvUtil.loadFromServiceLoader(Configurator.class); if (c != null) { try { c.setContext(loggerContext); c.configure(loggerContext); } catch (Exception e) { throw new LogbackException(String.format("Failed to initialize Configurator: %s using ServiceLoader", c != null ? c.getClass() .getCanonicalName() : "null"), e); } } else { //設置默認的config,後期日誌直接輸出到控制檯 BasicConfigurator basicConfigurator = new BasicConfigurator(); basicConfigurator.setContext(loggerContext); basicConfigurator.configure(loggerContext); } } }
下面分析下ContextInitializer.configureByResource(url):
public void configureByResource(URL url) throws JoranException { if (url == null) { throw new IllegalArgumentException("URL argument cannot be null"); } final String urlString = url.toString(); //logback配置是groovy文件 if (urlString.endsWith("groovy")) { if (EnvUtil.isGroovyAvailable()) { // avoid directly referring to GafferConfigurator so as to avoid // loading groovy.lang.GroovyObject . See also http://jira.qos.ch/browse/LBCLASSIC-214 GafferUtil.runGafferConfiguratorOn(loggerContext, this, url); } else { StatusManager sm = loggerContext.getStatusManager(); sm.add(new ErrorStatus("Groovy classes are not available on the class path. ABORTING INITIALIZATION.", loggerContext)); } } else if (urlString.endsWith("xml")) { //logback配置文件是xml。咱們主要分析該狀況下的配置 //首先建立一個配置器,設置好loggerContext後,根據url進行配置解析 JoranConfigurator configurator = new JoranConfigurator(); configurator.setContext(loggerContext); configurator.doConfigure(url); } else { throw new LogbackException("Unexpected filename extension of file [" + url.toString() + "]. Should be either .groovy or .xml"); } }
JoranConfigurator.doConfigure(url)代碼以下:
public final void doConfigure(URL url) throws JoranException { InputStream in = null; try { //將該url添加到watch list中 informContextOfURLUsedForConfiguration(getContext(), url); URLConnection urlConnection = url.openConnection(); // per http://jira.qos.ch/browse/LBCORE-105 // per http://jira.qos.ch/browse/LBCORE-127 //若是設置成true的話,當這個配置文件在一個jar中的時候,jar被鎖住。若是應用須要從新加載的話會出現問題。 //因此設置成false。 urlConnection.setUseCaches(false); in = urlConnection.getInputStream(); //將logback配置轉換成流進行解析 doConfigure(in); } catch (IOException ioe) { String errMsg = "Could not open URL [" + url + "]."; addError(errMsg, ioe); throw new JoranException(errMsg, ioe); } finally { if (in != null) { try { in.close(); } catch (IOException ioe) { String errMsg = "Could not close input stream"; addError(errMsg, ioe); throw new JoranException(errMsg, ioe); } } } } //將InputStream轉換成sax的InputSource對象,而後調用doConfigure進行解析 public final void doConfigure(InputStream inputStream) throws JoranException { doConfigure(new InputSource(inputStream)); } // this is the most inner form of doConfigure whereto other doConfigure // methods ultimately delegate public final void doConfigure(final InputSource inputSource) throws JoranException { long threshold = System.currentTimeMillis(); // if (!ConfigurationWatchListUtil.wasConfigurationWatchListReset(context)) { // informContextOfURLUsedForConfiguration(getContext(), null); // } SaxEventRecorder recorder = new SaxEventRecorder(context); //將xml的<tag>bodyStr</tag>解析成StartEvent,BodyEvent,EndEvent,保存到recorder.saxEventList中 //若是解析期間遇到warn、error、fatalError狀況則會生成信息存入StaticLoggerBinder.defaultLoggerContext.sm中 //後期將會將這些日誌輸出 recorder.recordEvents(inputSource); //根據xml每一個element對應的event來進行解析 doConfigure(recorder.saxEventList); // no exceptions a this level StatusUtil statusUtil = new StatusUtil(context); if (statusUtil.noXMLParsingErrorsOccurred(threshold)) { addInfo("Registering current configuration as safe fallback point"); registerSafeConfiguration(recorder.saxEventList); } } public void doConfigure(final List<SaxEvent> eventList) throws JoranException { //將大部分的element path包裝成ElementSelector並映射一個Action,好比xml中configuration/logger element path映射成LoggerAction //當解析到<configuration><logger>時會觸發LoggerAction的begin方法 //當解析到<configuration><logger>bodyStr 時會觸發LoggerAction的body方法,固然logger element是沒有body的 //當解析到<configuration><logger>bodyStr</logger> 時會觸發LoggerAction的end方法 //action的映射關係存在JoranConfigurator#interpreter中 buildInterpreter(); // disallow simultaneous configurations of the same context synchronized (context.getConfigurationLock()) { //解析eventList interpreter.getEventPlayer().play(eventList); } }
以下是一個簡單的logback.xml demo:
<?xml version="1.0" encoding="UTF-8"?> <configuration xmlns="http://www.padual.com/java/logback.xsd"> <appender name="WARN" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>/Users/apple/Documents/UNKONESERVER/warn.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- 按天回滾 daily --> <fileNamePattern>/Users/apple/Documents/UNKONESERVER/warn.%d{yyyy-MM-dd}.log</fileNamePattern> <!-- 日誌最大的保存天數 --> <maxHistory>2</maxHistory> </rollingPolicy> <encoder> <pattern>%msg%n</pattern> </encoder> </appender> <logger name="com.logback.test" level="debug" additivity="false"> <appender-ref ref="WARN" /> </logger> <root level="debug"> </root> </configuration>
轉換成SaxEvent list即是這個樣子:
eventList = {ArrayList@982} size = 26 0 = {StartEvent@999} "StartEvent(configuration) [2,63]" 1 = {StartEvent@1000} "StartEvent(appendername="WARN" class="ch.qos.logback.core.rolling.RollingFileAppender") [4,83]" 2 = {StartEvent@1001} "StartEvent(file) [5,15]" 3 = {BodyEvent@1002} "BodyEvent(/Users/apple/Documents/UNKONESERVER/warn.log)5,61" 4 = {EndEvent@1003} " EndEvent(file) [5,66]" 5 = {StartEvent@1004} "StartEvent(rollingPolicyclass="ch.qos.logback.core.rolling.TimeBasedRollingPolicy") [6,83]" 6 = {StartEvent@1005} "StartEvent(fileNamePattern) [8,30]" 7 = {BodyEvent@1006} "BodyEvent(/Users/apple/Documents/UNKONESERVER/warn.%d{yyyy-MM-dd}.log)8,91" 8 = {EndEvent@1007} " EndEvent(fileNamePattern) [8,107]" 9 = {StartEvent@1008} "StartEvent(maxHistory) [10,25]" 10 = {BodyEvent@1009} "BodyEvent(2)10,28" 11 = {EndEvent@1010} " EndEvent(maxHistory) [10,39]" 12 = {EndEvent@1011} " EndEvent(rollingPolicy) [11,25]" 13 = {StartEvent@1012} "StartEvent(encoder) [12,18]" 14 = {StartEvent@1013} "StartEvent(pattern) [13,22]" 15 = {BodyEvent@1014} "BodyEvent(%msg%n)13,30" 16 = {EndEvent@1015} " EndEvent(pattern) [13,38]" 17 = {EndEvent@1016} " EndEvent(encoder) [14,19]" 18 = {EndEvent@1017} " EndEvent(appender) [15,16]" 19 = {StartEvent@1018} "StartEvent(loggername="com.logback.test" level="debug" additivity="false") [18,32]" 20 = {StartEvent@1019} "StartEvent(appender-refref="WARN") [19,36]" 21 = {EndEvent@1020} " EndEvent(appender-ref) [19,36]" 22 = {EndEvent@1021} " EndEvent(logger) [20,14]" 23 = {StartEvent@1022} "StartEvent(rootlevel="debug") [23,25]" 24 = {EndEvent@1023} " EndEvent(root) [25,12]" 25 = {EndEvent@1024} " EndEvent(configuration) [27,17]"
對應26個event。 下面分析EventPlayer中play方法如何對event list進行遍歷處理。 很清晰的看到event分紅三種:StartEvent、BodyEvent、EndEvent。這個三種最終會調用對應action的begin、body、end方法:
public void play(List<SaxEvent> aSaxEventList) { eventList = aSaxEventList; SaxEvent se; for (currentIndex = 0; currentIndex < eventList.size(); currentIndex++) { se = eventList.get(currentIndex); if (se instanceof StartEvent) { interpreter.startElement((StartEvent) se); // invoke fireInPlay after startElement processing interpreter.getInterpretationContext().fireInPlay(se); } if (se instanceof BodyEvent) { // invoke fireInPlay before characters processing interpreter.getInterpretationContext().fireInPlay(se); interpreter.characters((BodyEvent) se); } if (se instanceof EndEvent) { // invoke fireInPlay before endElement processing interpreter.getInterpretationContext().fireInPlay(se); interpreter.endElement((EndEvent) se); } } }
public void startElement(StartEvent se) { //設置event在xml中的locator信息,好比在多少行多少列 setDocumentLocator(se.getLocator()); //處理start event startElement(se.namespaceURI, se.localName, se.qName, se.attributes); } private void startElement(String namespaceURI, String localName, String qName, Attributes atts) { //首先選擇localName爲tagName,若是localName爲空則選擇qName String tagName = getTagName(localName, qName); //將這個startEvent的tagName推入elementPath棧中,後期處理對應的endEvent時,觸發endElement方法彈出該tagName elementPath.push(tagName); if (skip != null) { // every startElement pushes an action list pushEmptyActionList(); return; } //根據elementPath查找相應的action list, //好比elementPath推入了兩個數據[configuration][appender],則會找到AppenderAction。 //Interpreter中ruleStore並不維護全部elementPath對應的action, //好比[configuration][appender][file],在ruleStore中找不到對應的action。 //logback提供了兩種隱式action:NestedComplexPropertyIA、NestedBasicPropertyIA, //1.[configuration][appender][file]對應的action是NestedBasicPropertyIA, //由於它屬於簡單的內嵌式action,file屬於appender的一個屬性,而file element沒有內嵌element,只有一個body。 //2.[configuration][appender][rollingPolicy]對應的action是NestedComplexPropertyIA, //由於它屬於複雜的內嵌式action,rollingPolicy屬於appender的一個屬性,可是,rollingPolicy element還擁有本身的內嵌element List<Action> applicableActionList = getApplicableActionList(elementPath, atts); if (applicableActionList != null) { //將action list推入actionlistStacl棧中,後續會用到 actionListStack.add(applicableActionList); //遍歷applicableActionList,觸發每一個action的begin方法,正常applicableActionList中只有一個元素 callBeginAction(applicableActionList, tagName, atts); } else { // every startElement pushes an action list pushEmptyActionList(); String errMsg = "no applicable action for [" + tagName + "], current ElementPath is [" + elementPath + "]"; cai.addError(errMsg); } }
void callBeginAction(List<Action> applicableActionList, String tagName, Attributes atts) { if (applicableActionList == null) { return; } Iterator<Action> i = applicableActionList.iterator(); while (i.hasNext()) { Action action = (Action) i.next(); // now let us invoke the action. We catch and report any eventual // exceptions try { //真正的處理start event的邏輯,即該element path對應action的begin方法 action.begin(interpretationContext, tagName, atts); } catch (ActionException e) { skip = elementPath.duplicate(); cai.addError("ActionException in Action for tag [" + tagName + "]", e); } catch (RuntimeException e) { skip = elementPath.duplicate(); cai.addError("RuntimeException in Action for tag [" + tagName + "]", e); } } }
public void characters(BodyEvent be) { setDocumentLocator(be.locator); //獲取body文本 String body = be.getText(); List<Action> applicableActionList = actionListStack.peek(); if (body != null) { body = body.trim(); if (body.length() > 0) { // System.out.println("calling body method with ["+body+ "]"); callBodyAction(applicableActionList, body); } } } private void callBodyAction(List<Action> applicableActionList, String body) { if (applicableActionList == null) { return; } Iterator<Action> i = applicableActionList.iterator(); while (i.hasNext()) { Action action = i.next(); try { //觸發該element path對應action的body方法 action.body(interpretationContext, body); } catch (ActionException ae) { cai.addError("Exception in end() methd for action [" + action + "]", ae); } } }
public void endElement(EndEvent endEvent) { setDocumentLocator(endEvent.locator); endElement(endEvent.namespaceURI, endEvent.localName, endEvent.qName); } private void endElement(String namespaceURI, String localName, String qName) { // given that an action list is always pushed for every startElement, we // need // to always pop for every endElement List<Action> applicableActionList = (List<Action>) actionListStack.pop(); if (skip != null) { if (skip.equals(elementPath)) { skip = null; } } else if (applicableActionList != EMPTY_LIST) { callEndAction(applicableActionList, getTagName(localName, qName)); } // given that we always push, we must also pop the pattern //把在startElement方法中推入的元素彈出來 elementPath.pop(); } private void callEndAction(List<Action> applicableActionList, String tagName) { if (applicableActionList == null) { return; } // logger.debug("About to call end actions on node: [" + localName + "]"); Iterator<Action> i = applicableActionList.iterator(); while (i.hasNext()) { Action action = i.next(); // now let us invoke the end method of the action. We catch and report // any eventual exceptions try { //觸發當前element path對應action的end方法 action.end(interpretationContext, tagName); } catch (ActionException ae) { // at this point endAction, there is no point in skipping children as // they have been already processed cai.addError("ActionException in Action for tag [" + tagName + "]", ae); } catch (RuntimeException e) { // no point in setting skip cai.addError("RuntimeException in Action for tag [" + tagName + "]", e); } } }