CoderLi

V1

2022/10/30阅读:10主题:极客黑

Logback 相关组件

启动流程

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

然后回去找 ILoggerFactory 接口

ILoggerFactory iLoggerFactory = getILoggerFactory();

获取 SLF4JServiceProvider

    return getProvider().getLoggerFactory();

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

        List\<SLF4JServiceProvider> providersList = findServiceProviders();

那么就会通过 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();
}
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);
    }
}

这个初始化 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);
        }
    }
}

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

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

如果有则使用配置文件。没有则通过 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");
    }
}

对配置文件的节点解释

微信公众号:CoderLi
微信公众号:CoderLi
微信公众号: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);
}

相关组件

Logger 怎么与 Appender 关联上的?

微信公众号:CoderLi
微信公众号:CoderLi
微信公众号:CoderLi
微信公众号:CoderLi

Appender

微信公众号:CoderLi
微信公众号:CoderLi
微信公众号:CoderLi
微信公众号:CoderLi
微信公众号:CoderLi
微信公众号:CoderLi

看看它的继承结构

微信公众号:CoderLi
微信公众号:CoderLi

UnsynchronizedAppenderBase

微信公众号:CoderLi
微信公众号:CoderLi

直接看看 doAppend 方法

微信公众号:CoderLi
微信公众号:CoderLi

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

OutputStreamAppender

增加了另一个组件来帮忙

protected Encoder<E> encoder;
@Override
protected void append(E eventObject) {
    if (!isStarted()) {
        return;
    }

    subAppend(eventObject);
}
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));
    }
}
protected void writeOut(E event) throws IOException {
    byte[] byteArray = this.encoder.encode(event);
    writeBytes(byteArray);
}
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();
    }
}

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

Encoder

微信公众号:CoderLi
微信公众号:CoderLi

继承结构

微信公众号:CoderLi
微信公众号:CoderLi

EncoderBase

abstract public class EncoderBase<Eextends ContextAwareBase implements Encoder<E{

    protected boolean started;

    public boolean isStarted() {
        return started;
    }

    public void start() {
        started = true;
    }

    public void stop() {
        started = false;
    }
}

LayoutWrappingEncoder

public class LayoutWrappingEncoder<Eextends EncoderBase<E{

    protected Layout<E> layout;

    /**
     * The charset to use when converting a String into bytes.
     * <p>
     * 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);
    }
}
    

这里出现了另一个组件 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();
    }

}

Layout

微信公众号:CoderLi
微信公众号:CoderLi

看下其继承结构

微信公众号:CoderLi
微信公众号:CoderLi

LayoutBase

微信公众号:CoderLi
微信公众号:CoderLi

PatternLayoutBase

abstract public class PatternLayoutBase<Eextends 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;

这里出现另一个组件 Converter

PatternLayout

微信公众号:CoderLi
微信公众号:CoderLi
public String doLayout(ILoggingEvent event) {
    if (!isStarted()) {
        return CoreConstants.EMPTY_STRING;
    }
    return writeLoopOnConverters(event);
}
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();
}

Converter

微信公众号:CoderLi
微信公众号:CoderLi
微信公众号: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;
        }
    }
}

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

分类:

后端

标签:

Java

作者介绍

CoderLi
V1