• Logback 相关组件


    启动流程

    Logger logger = LoggerFactory.getLogger(Main1.class);

    然后回去找 ILoggerFactory 接口

    ILoggerFactory iLoggerFactory = getILoggerFactory();

    获取 SLF4JServiceProvider

        return getProvider().getLoggerFactory();
    
    • 1

    如果 slf4j 没有进行初始化、也就是没有绑定到底使用哪个日志框架

            List\ providersList = findServiceProviders();
    
    • 1

    那么就会通过 SPI 获取对应的 ServiceProvider

    PROVIDER.initialize();

    调用其初始化方法

    @Override
    public void initialize() {
        defaultLoggerContext = new LoggerContext();
        defaultLoggerContext.setName(CoreConstants.DEFAULT_CONTEXT_NAME);
        initializeLoggerContext();
        defaultLoggerContext.start();
        markerFactory = new BasicMarkerFactory();
        mdcAdapter = new LogbackMDCAdapter();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    private void initializeLoggerContext() {
        try {
            try {
                new ContextInitializer(defaultLoggerContext).autoConfig();
            } catch (JoranException je) {
                Util.report("Failed to auto configure default logger context", je);
            }
            // LOGBACK-292
            if (!StatusUtil.contextHasStatusListener(defaultLoggerContext)) {
                StatusPrinter.printInCaseOfErrorsOrWarnings(defaultLoggerContext);
            }
            // contextSelectorBinder.init(defaultLoggerContext, KEY);
    
        } catch (Exception t) { // see LOGBACK-1159
            Util.report("Failed to instantiate [" + LoggerContext.class.getName() + "]", t);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    这个初始化 LoggerContext 其实是去找配置文件

    new ContextInitializer(defaultLoggerContext).autoConfig();

    public void autoConfig() throws JoranException {
        StatusListenerConfigHelper.installIfAsked(loggerContext);
        URL url = findURLOfDefaultConfigurationFile(true);
        if (url != null) {
            configureByResource(url);
        } else {
            Configurator c = ClassicEnvUtil.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 {
                BasicConfigurator basicConfigurator = new BasicConfigurator();
                basicConfigurator.setContext(loggerContext);
                basicConfigurator.configure(loggerContext);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    findURLOfDefaultConfigurationFile 会找两个默认的配置文件

    final public static String AUTOCONFIG_FILE = "logback.xml";
    final public static String TEST_AUTOCONFIG_FILE = "logback-test.xml";
    
    • 1
    • 2

    如果有则使用配置文件。没有则通过 SPI 找下 Configurator 的实现类。如果都没有则使用默认的配置类 BasicConfigurator

    public void configureByResource(URL url) throws JoranException {
        if (url == null) {
            throw new IllegalArgumentException("URL argument cannot be null");
        }
        final String urlString = url.toString();
        if (urlString.endsWith("xml")) {
            JoranConfigurator configurator = new JoranConfigurator();
            configurator.setContext(loggerContext);
            configurator.doConfigure(url);
        } else {
            throw new LogbackException(
                    "Unexpected filename extension of file [" + url.toString() + "]. Should be .xml");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    对配置文件的节点解释

    微信公众号:CoderLi

    微信公众号:CoderLi

    我们看看 root 标签的处理 ch.qos.logback.classic.model.processor.RootLoggerModelHandler

    @Override
    public void handle(ModelInterpretationContext mic, Model model) throws ModelHandlerException {
        inError = false;
    
        RootLoggerModel rootLoggerModel = (RootLoggerModel) model;
    
        LoggerContext loggerContext = (LoggerContext) this.context;
        root = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME);
    
        String levelStr = mic.subst(rootLoggerModel.getLevel());
        if (!OptionHelper.isNullOrEmpty(levelStr)) {
            Level level = Level.toLevel(levelStr);
            addInfo("Setting level of ROOT logger to " + level);
            root.setLevel(level);
        }
    
        mic.pushObject(root);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    相关组件

    Logger 怎么与 Appender 关联上的?

    微信公众号:CoderLi

    微信公众号:CoderLi

    Appender

    微信公众号:CoderLi

    微信公众号:CoderLi

    微信公众号:CoderLi

    看看它的继承结构

    微信公众号:CoderLi

    UnsynchronizedAppenderBase

    微信公众号:CoderLi

    直接看看 doAppend 方法

    微信公众号:CoderLi

    看到通过 ThreadLocal 保证不被多次执行

    OutputStreamAppender

    增加了另一个组件来帮忙

    protected Encoder<E> encoder;
    
    • 1
    @Override
    protected void append(E eventObject) {
        if (!isStarted()) {
            return;
        }
    
        subAppend(eventObject);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    protected void subAppend(E event) {
        if (!isStarted()) {
            return;
        }
        try {
            // this step avoids LBCLASSIC-139
            if (event instanceof DeferredProcessingAware) {
                ((DeferredProcessingAware) event).prepareForDeferredProcessing();
            }
            writeOut(event);
    
        } catch (IOException ioe) {
            // as soon as an exception occurs, move to non-started state
            // and add a single ErrorStatus to the SM.
            this.started = false;
            addStatus(new ErrorStatus("IO failure in appender", this, ioe));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    protected void writeOut(E event) throws IOException {
        byte[] byteArray = this.encoder.encode(event);
        writeBytes(byteArray);
    }
    
    • 1
    • 2
    • 3
    • 4
    private void writeBytes(byte[] byteArray) throws IOException {
        if (byteArray == null || byteArray.length == 0)
            return;
    
        lock.lock();
        try {
            this.outputStream.write(byteArray);
            if (immediateFlush) {
                this.outputStream.flush();
            }
        } finally {
            lock.unlock();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    剩余的 Appender 子类不做介绍了、直接转到另一个组件中去了

    Encoder

    微信公众号:CoderLi

    继承结构

    微信公众号:CoderLi

    EncoderBase

    abstract public class EncoderBase<E> extends ContextAwareBase implements Encoder<E> {
    
        protected boolean started;
    
        public boolean isStarted() {
            return started;
        }
    
        public void start() {
            started = true;
        }
    
        public void stop() {
            started = false;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    LayoutWrappingEncoder

    public class LayoutWrappingEncoder<E> extends EncoderBase<E> {
    
        protected Layout<E> layout;
    
        /**
         * The charset to use when converting a String into bytes.
         * 

    * By default this property has the value {@code null} which corresponds to the * system's default charset. */ private Charset charset; ContextAware parent; Boolean immediateFlush = null; public byte[] encode(E event) { String txt = layout.doLayout(event); return convertToBytes(txt); } }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    这里出现了另一个组件 Layout

    PatternLayoutEncoder

    public class PatternLayoutEncoder extends PatternLayoutEncoderBase<ILoggingEvent> {
    
        @Override
        public void start() {
            PatternLayout patternLayout = new PatternLayout();
            patternLayout.setContext(context);
            patternLayout.setPattern(getPattern());
            patternLayout.setOutputPatternAsHeader(outputPatternAsHeader);
            patternLayout.start();
            this.layout = patternLayout;
            super.start();
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    Layout

    微信公众号:CoderLi

    看下其继承结构

    微信公众号:CoderLi

    LayoutBase

    微信公众号:CoderLi

    PatternLayoutBase

    abstract public class PatternLayoutBase<E> extends LayoutBase<E> {
    
        static final int INTIAL_STRING_BUILDER_SIZE = 256;
        Converter<E> head;
        String pattern;
        protected PostCompileProcessor<E> postCompileProcessor;
    
        Map<String, String> instanceConverterMap = new HashMap<String, String>();
        protected boolean outputPatternAsHeader = false;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    这里出现另一个组件 Converter

    PatternLayout

    微信公众号:CoderLi

    public String doLayout(ILoggingEvent event) {
        if (!isStarted()) {
            return CoreConstants.EMPTY_STRING;
        }
        return writeLoopOnConverters(event);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    protected String writeLoopOnConverters(E event) {
        StringBuilder strBuilder = new StringBuilder(INTIAL_STRING_BUILDER_SIZE);
        Converter<E> c = head;
        while (c != null) {
            c.write(strBuilder, event);
            c = c.getNext();
        }
        return strBuilder.toString();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    Converter

    微信公众号:CoderLi

    微信公众号:CoderLi

    我们直接看下 MDCConverter

    @Override
    public String convert(ILoggingEvent event) {
        Map<String, String> mdcPropertyMap = event.getMDCPropertyMap();
    
        if (mdcPropertyMap == null) {
            return defaultValue;
        }
    
        if (key == null) {
            return outputMDCForAllKeys(mdcPropertyMap);
        } else {
    
            String value = mdcPropertyMap.get(key);
            if (value != null) {
                return value;
            } else {
                return defaultValue;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    非常简单、最终是根据 MDCAdapter 获取对应的属性值

    本文由mdnice多平台发布

  • 相关阅读:
    深度学习动物识别 - 卷积神经网络 机器视觉 图像识别 计算机竞赛
    Python如何调用C和C++
    面试题四:请解释一下watch,computed和filter之间的区别
    模板字面量(Template literals)介绍
    JQuery系列之样式操作
    java一个切面如何作用做个注解
    CentOS7.6(Linux)环境下有网和无网安装Docker
    My Seventieth Page - 最后一块石头的重量Ⅱ - By Nicolas
    【C++高阶(三)】AVL树深度剖析&模拟实现
    Kafka入门二——SpringBoot连接Kafka示例
  • 原文地址:https://blog.csdn.net/lijinxiong520/article/details/127604819