logback源码阅读-配置文件解析过程
Posted lqblog
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了logback源码阅读-配置文件解析过程相关的知识,希望对你有一定的参考价值。
前面介绍了logback源码初始化过程是委托给ContextInitializer
StaticLoggerBinder
void init() { try { try { (new ContextInitializer(this.defaultLoggerContext)).autoConfig(); } catch (JoranException var2) { Util.report("Failed to auto configure default logger context", var2); } if (!StatusUtil.contextHasStatusListener(this.defaultLoggerContext)) { StatusPrinter.printInCaseOfErrorsOrWarnings(this.defaultLoggerContext); } this.contextSelectorBinder.init(this.defaultLoggerContext, KEY); this.initialized = true; } catch (Exception var3) { Util.report("Failed to instantiate [" + LoggerContext.class.getName() + "]", var3); } }
ContextInitializer
org.slf4j.impl.StaticLoggerBinder#init
->
ch.qos.logback.classic.util.ContextInitializer#autoConfig
public void autoConfig() throws JoranException { /** * <1>这里配置监听查找配置的消息 * 判断系统变量是否有-Dlogback.statusListenerClass 参数 * 等于SYSOUT 则默认使用OnConsoleStatusListener 这个是控制台打印 * 否则当做配置的全路径(我们自己创建一个StatusListener实现类) */ StatusListenerConfigHelper.installIfAsked(this.loggerContext); /** *这里会依次查找 * <2>1.从系统变量查找配置文件-Dlogback.configurationFile={file} * 2.如果没有配置则依次找logback-test.xml logback.groovy logback.xml 找到任意一个返回 */ URL url = this.findURLOfDefaultConfigurationFile(true); if (url != null) { //找到配置文件则走配置文件解析配置 this.configureByResource(url); } else { /** * 这里主要是java的SPI扩展点ServiceLoader 如果想实现自己的配置文件定义 可以通过这个做扩展 */ Configurator c = (Configurator) EnvUtil.loadFromServiceLoader(Configurator.class); if (c != null) { try { c.setContext(this.loggerContext); c.configure(this.loggerContext); } catch (Exception var4) { throw new LogbackException(String.format("Failed to initialize Configurator: %s using ServiceLoader", c != null ? c.getClass().getCanonicalName() : "null"), var4); } } else { //没有SPI扩展 则使用默认的配置 SPI扩展可以参考介个 BasicConfigurator basicConfigurator = new BasicConfigurator(); basicConfigurator.setContext(this.loggerContext); basicConfigurator.configure(this.loggerContext); } } }
<1>处主要是配置查找日志文件的监听器默认是控制台打印
<2>处
configureByResource
org.slf4j.impl.StaticLoggerBinder#init
->
ch.qos.logback.classic.util.ContextInitializer#autoConfig#configureByResource
public void configureByResource(URL url) throws JoranException { if (url == null) { throw new IllegalArgumentException("URL argument cannot be null"); } else { String urlString = url.toString(); //如果配置文件是groovy结尾 按照groovy的解析防止 一般我们都是用xml所以不看这一步 if (urlString.endsWith("groovy")) { if (EnvUtil.isGroovyAvailable()) { GafferUtil.runGafferConfiguratorOn(this.loggerContext, this, url); } else { StatusManager sm = this.loggerContext.getStatusManager(); sm.add(new ErrorStatus("Groovy classes are not available on the class path. ABORTING INITIALIZATION.", this.loggerContext)); } } else { if (!urlString.endsWith("xml")) { throw new LogbackException("Unexpected filename extension of file [" + url.toString() + "]. Should be either .groovy or .xml"); } //如果是xml结尾则走xml的解析方式 委托给了JoranConfigurator JoranConfigurator configurator = new JoranConfigurator(); configurator.setContext(this.loggerContext); configurator.doConfigure(url); } } }
JoranConfigurator
类图
doConfigure
ch.qos.logback.core.joran.GenericConfigurator#doConfigure(java.net.URL)
public final void doConfigure(URL url) throws JoranException { InputStream in = null; boolean var12 = false; String errMsg; try { var12 = true; //暂时也不知道干啥的 informContextOfURLUsedForConfiguration(this.getContext(), url); //获得一个连接对象 这里是FileURLConnection 可以想象是否可以支持http呢 就可以远程配置文件了 URLConnection urlConnection = url.openConnection(); //不使用缓存 urlConnection.setUseCaches(false); //获取流 in = urlConnection.getInputStream(); //接下来看这个方法处理 this.doConfigure(in, url.toExternalForm()); var12 = false; } catch (IOException var15) { errMsg = "Could not open URL [" + url + "]."; this.addError(errMsg, var15); throw new JoranException(errMsg, var15); } finally { if (var12) { if (in != null) { try { in.close(); } catch (IOException var13) { String errMsg = "Could not close input stream"; this.addError(errMsg, var13); throw new JoranException(errMsg, var13); } } } } if (in != null) { try { in.close(); } catch (IOException var14) { errMsg = "Could not close input stream"; this.addError(errMsg, var14); throw new JoranException(errMsg, var14); } } }
ch.qos.logback.core.joran.GenericConfigurator#doConfigure#doConfigure
public final void doConfigure(InputSource inputSource) throws JoranException { long threshold = System.currentTimeMillis(); SaxEventRecorder recorder = new SaxEventRecorder(this.context); //<1>这里利用Sax解析xml 并封装成SaxEvent recorder.recordEvents(inputSource); //<2>将封装成java对象的SaxEvent进行配置处理 this.doConfigure(recorder.saxEventList); StatusUtil statusUtil = new StatusUtil(this.context); if (statusUtil.noXMLParsingErrorsOccurred(threshold)) { this.addInfo("Registering current configuration as safe fallback point"); this.registerSafeConfiguration(recorder.saxEventList); } }
<1>
ch.qos.logback.core.joran.event.SaxEventRecorder#recordEvents
public List<SaxEvent> recordEvents(InputSource inputSource) throws JoranException { SAXParser saxParser = this.buildSaxParser(); try { //这里因为当前类继承了DefaultHandeler 所以通过这里将SAX解析成当前出席需要的对象 saxParser.parse(inputSource, this); return this.saxEventList; } catch (IOException var4) { this.handleError("I/O error occurred while parsing xml file", var4); } catch (SAXException var5) { throw new JoranException("Problem parsing XML document. See previously reported errors.", var5); } catch (Exception var6) { this.handleError("Unexpected exception while parsing XML document.", var6); } } //解析开始标签 public void startElement(String namespaceURI, String localName, String qName, Attributes atts) { String tagName = this.getTagName(localName, qName); this.globalElementPath.push(tagName); ElementPath current = this.globalElementPath.duplicate(); this.saxEventList.add(new StartEvent(current, namespaceURI, localName, qName, atts, this.getLocator())); } //解析结束标签 public void endElement(String namespaceURI, String localName, String qName) { this.saxEventList.add(new EndEvent(namespaceURI, localName, qName, this.getLocator())); this.globalElementPath.pop(); }
<2>处代码
doConfigure
public void doConfigure(List<SaxEvent> eventList) throws JoranException { //这里主要是初始化interpreter内部维护配置文件每个标签解析action的关系 this.buildInterpreter(); synchronized(this.context.getConfigurationLock()) { //开始映射对应的action进行解析 this.interpreter.getEventPlayer().play(eventList); } }
ch.qos.logback.core.joran.GenericConfigurator#buildInterpreter
protected void buildInterpreter() { RuleStore rs = new SimpleRuleStore(this.context); //添加action映射关系 action为对应标签的初始化方式 子类实现 this.addInstanceRules(rs); this.interpreter = new Interpreter(this.context, rs, this.initialElementPath()); InterpretationContext interpretationContext = this.interpreter.getInterpretationContext(); interpretationContext.setContext(this.context); this.addImplicitRules(this.interpreter); this.addDefaultNestedComponentRegistryRules(interpretationContext.getDefaultNestedComponentRegistry()); }
ch.qos.logback.classic.joran.JoranConfigurator
可以参考一下 实现自定义属性
public void addInstanceRules(RuleStore rs) {
//父类路由 比如Appender就在父类追加的 super.addInstanceRules(rs); rs.addRule(new ElementSelector("configuration"), new ConfigurationAction()); rs.addRule(new ElementSelector("configuration/contextName"), new ContextNameAction()); rs.addRule(new ElementSelector("configuration/contextListener"), new LoggerContextListenerAction()); rs.addRule(new ElementSelector("configuration/insertFromJNDI"), new InsertFromJNDIAction()); rs.addRule(new ElementSelector("configuration/evaluator"), new EvaluatorAction()); rs.addRule(new ElementSelector("configuration/appender/sift"), new SiftAction()); rs.addRule(new ElementSelector("configuration/appender/sift/*"), new NOPAction()); rs.addRule(new ElementSelector("configuration/logger"), new LoggerAction()); rs.addRule(new ElementSelector("configuration/logger/level"), new LevelAction()); rs.addRule(new ElementSelector("configuration/root"), new RootLoggerAction()); rs.addRule(new ElementSelector("configuration/root/level"), new LevelAction()); rs.addRule(new ElementSelector("configuration/logger/appender-ref"), new AppenderRefAction()); rs.addRule(new ElementSelector("configuration/root/appender-ref"), new AppenderRefAction()); rs.addRule(new ElementSelector("*/if"), new IfAction()); rs.addRule(new ElementSelector("*/if/then"), new ThenAction()); rs.addRule(new ElementSelector("*/if/then/*"), new NOPAction()); rs.addRule(new ElementSelector("*/if/else"), new ElseAction()); rs.addRule(new ElementSelector("*/if/else/*"), new NOPAction()); if (PlatformInfo.hasJMXObjectName()) { rs.addRule(new ElementSelector("configuration/jmxConfigurator"), new JMXConfiguratorAction()); } rs.addRule(new ElementSelector("configuration/include"), new IncludeAction()); rs.addRule(new ElementSelector("configuration/consolePlugin"), new ConsolePluginAction()); rs.addRule(new ElementSelector("configuration/receiver"), new ReceiverAction()); }
具体扩展可以参考一下appender实现 当我们需要自定义某个组件的时候也需要看一下对应action源码 比如appenderAction 实现了某个接口就调用start方法 内部可能给我们预留很多扩展
以上是关于logback源码阅读-配置文件解析过程的主要内容,如果未能解决你的问题,请参考以下文章
日志 Logback 配置文件这么写,TPS 提高 10 倍