slf4j+logback日志框架源码解析

slf4j+logback日志框架源码解析

项目搭建

使用idea工具新建maven项目,引入依赖:

	 <dependencies>
             <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>1.5.15.RELEASE</version>
        </dependency>
    </dependencies>

日志使用场景(源码入口)

private Logger logger = LoggerFactory.getLogger(UserController.class);

slf4j源码

(1)调用静态方法getLogger方法,根据类名称获取logger对象

public static Logger getLogger(Class<?> clazz) {
    Logger logger = getLogger(clazz.getName());
    if (DETECT_LOGGER_NAME_MISMATCH) {
        Class<?> autoComputedCallingClass = Util.getCallingClass();
        if (autoComputedCallingClass != null && nonMatchingClasses(clazz, autoComputedCallingClass)) {
            Util.report(String.format("Detected logger name mismatch. Given name: \"%s\"; computed name: \"%s\".", logger.getName(),
                            autoComputedCallingClass.getName()));
            Util.report("See " + LOGGER_NAME_MISMATCH_URL + " for an explanation");
        }
    }
    return logger;
}

(2)要获取logger对象,首先需要获取ILoggerFactory对象

public static Logger getLogger(String name) {
    ILoggerFactory iLoggerFactory = getILoggerFactory();
    return iLoggerFactory.getLogger(name);
}

(3)调用getILoggerFactory方法获取ILoggerFactory对象,INITIALIZATION_STATE为初始化状态,有5中状态,0-未初始化;1-正在初始化;2-无获取单例对象的方法;3-初始化成功;4-无操作记录器实现。初次调用,初始化状态为0,在方法中会将初始化状态置为1,并进行初始化

public static ILoggerFactory getILoggerFactory() {
    if (INITIALIZATION_STATE == UNINITIALIZED) {
        synchronized (LoggerFactory.class) {
            if (INITIALIZATION_STATE == UNINITIALIZED) {
                INITIALIZATION_STATE = ONGOING_INITIALIZATION;
                performInitialization();
            }
        }
    }
    switch (INITIALIZATION_STATE) {
    case SUCCESSFUL_INITIALIZATION:
        return StaticLoggerBinder.getSingleton().getLoggerFactory();
    case NOP_FALLBACK_INITIALIZATION:
        return NOP_FALLBACK_FACTORY;
    case FAILED_INITIALIZATION:
        throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG);
    case ONGOING_INITIALIZATION:
        // support re-entrant behavior.
        // See also http://jira.qos.ch/browse/SLF4J-97
        return SUBST_FACTORY;
    }
    throw new IllegalStateException("Unreachable code");
}

(4)初始化分为两个步骤:1.绑定StaticLoggerBinder;2.版本兼容校验

private final static void performInitialization() {
    bind();
    if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) {
        versionSanityCheck();
    }
}

a.绑定,验证是否存在org/slf4j/impl/StaticLoggerBinder类,以及是否存在多个StaticLoggerBinder,并初始化StaticLoggerBinder的单例对象,更新初始化状态为成功

private final static void bind() {
    try {
        Set<URL> staticLoggerBinderPathSet = null;
        // skip check under android, see also
        // http://jira.qos.ch/browse/SLF4J-328
        if (!isAndroid()) {
            staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
            reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
        }
        // the next line does the binding
        StaticLoggerBinder.getSingleton();
        INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
        reportActualBinding(staticLoggerBinderPathSet);
        fixSubstituteLoggers();
        replayEvents();
        // release all resources in SUBST_FACTORY
        SUBST_FACTORY.clear();
    } 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.contains("org.slf4j.impl.StaticLoggerBinder.getSingleton()")) {
            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);
    }
}

b.版本兼容性校验,笔者使用的slf4j-api:1.7.25,其兼容的StaticLoggerBinder类的版本是以1.6或1.7开头的

private final static void versionSanityCheck() {
    try {
        String requested = StaticLoggerBinder.REQUESTED_API_VERSION;

        boolean match = false;
        for (String aAPI_COMPATIBILITY_LIST : API_COMPATIBILITY_LIST) {
            if (requested.startsWith(aAPI_COMPATIBILITY_LIST)) {
                match = true;
            }
        }
        if (!match) {
            Util.report("The requested version " + requested + " by your slf4j binding is not compatible with "
                            + Arrays.asList(API_COMPATIBILITY_LIST).toString());
            Util.report("See " + VERSION_MISMATCH + " for further details.");
        }
    } catch (java.lang.NoSuchFieldError nsfe) {
        // given our large user base and SLF4J's commitment to backward
        // compatibility, we cannot cry here. Only for implementations
        // which willingly declare a REQUESTED_API_VERSION field do we
        // emit compatibility warnings.
    } catch (Throwable e) {
        // we should never reach here
        Util.report("Unexpected problem occured during version sanity check", e);
    }
}

logback源码

(1)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);
    }

}

(2)自动配置,获取配置文件

public void autoConfig() throws JoranException {
    StatusListenerConfigHelper.installIfAsked(this.loggerContext);
    URL url = this.findURLOfDefaultConfigurationFile(true);
    if (url != null) {
        this.configureByResource(url);
    } else {
        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 {
            BasicConfigurator basicConfigurator = new BasicConfigurator();
            basicConfigurator.setContext(this.loggerContext);
            basicConfigurator.configure(this.loggerContext);
        }
    }

}

a.以logback-test.xmllogback.groovylogback.xml的顺序查找日志配置文件

public URL findURLOfDefaultConfigurationFile(boolean updateStatus) {
    ClassLoader myClassLoader = Loader.getClassLoaderOfObject(this);
    URL url = this.findConfigFileURLFromSystemProperties(myClassLoader, updateStatus);
    if (url != null) {
        return url;
    } else {
        url = this.getResource("logback-test.xml", myClassLoader, updateStatus);
        if (url != null) {
            return url;
        } else {
            url = this.getResource("logback.groovy", myClassLoader, updateStatus);
            return url != null ? url : this.getResource("logback.xml", myClassLoader, updateStatus);
        }
    }
}

b.获取配置文件url,例如:file:/E:/workSpace/ops/target/classes/logback.xml

private URL getResource(String filename, ClassLoader myClassLoader, boolean updateStatus) {
    URL url = Loader.getResource(filename, myClassLoader);
    if (updateStatus) {
        this.statusOnResourceSearch(filename, myClassLoader, url);
    }

    return url;
}

(3)解析xml日志配置文件,新建SaxEventRecorder解析器,实现默认解析器DefaultHandler,复写startDocument()、startElement()、characters()、endElement()方法,添加Locator locator;属性,将解析的数据存储在saxEventList集合中,根据节点的不同位置,分别为StartEvent、EndEvent、BodyEvent对象

public class SaxEventRecorder extends DefaultHandler implements ContextAware {
    final ContextAwareImpl cai;
    public List<SaxEvent> saxEventList = new ArrayList();
    Locator locator;
    ElementPath globalElementPath = new ElementPath();

    public SaxEventRecorder(Context context) {
        this.cai = new ContextAwareImpl(context, this);
    }

    public final void recordEvents(InputStream inputStream) throws JoranException {
        this.recordEvents(new InputSource(inputStream));
    }

    public List<SaxEvent> recordEvents(InputSource inputSource) throws JoranException {
        SAXParser saxParser = this.buildSaxParser();

        try {
            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);
        }

        throw new IllegalStateException("This point can never be reached");
    }

(4)将解析出的数据进行初始化配置,buildInterpreter()方法用来新建Interpreter对象,logback依赖这个类进行初始化工作;play()遍历xml文件中的数据,进行配置

public void doConfigure(List<SaxEvent> eventList) throws JoranException {
    this.buildInterpreter();
    synchronized(this.context.getConfigurationLock()) {
        this.interpreter.getEventPlayer().play(eventList);
    }
}

a.RuleStore类添加匹配规则,当xml文件中的节点名称不对时,报错

public void addInstanceRules(RuleStore rs) {
    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());
}

b.针对StartElement对象,调用callBeginAction()方法

private void startElement(String namespaceURI, String localName, String qName, Attributes atts) {
    String tagName = this.getTagName(localName, qName);
    this.elementPath.push(tagName);
    if (this.skip != null) {
        this.pushEmptyActionList();
    } else {
        List<Action> applicableActionList = this.getApplicableActionList(this.elementPath, atts);
        if (applicableActionList != null) {
            this.actionListStack.add(applicableActionList);
            this.callBeginAction(applicableActionList, tagName, atts);
        } else {
            this.pushEmptyActionList();
            String errMsg = "no applicable action for [" + tagName + "], current ElementPath  is [" + this.elementPath + "]";
            this.cai.addError(errMsg);
        }

    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值