源码基于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); } } }