likes
comments
collection
share

Logback 相关组件

作者站长头像
站长
· 阅读数 16

启动流程

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");
    }
}

对配置文件的节点解释

Logback 相关组件

Logback 相关组件

我们看看 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 关联上的?

Logback 相关组件

Logback 相关组件

Appender

Logback 相关组件

Logback 相关组件

Logback 相关组件

看看它的继承结构

Logback 相关组件

UnsynchronizedAppenderBase

Logback 相关组件

直接看看 doAppend 方法

Logback 相关组件

看到通过 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

Logback 相关组件

继承结构

Logback 相关组件

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;
    }
}

LayoutWrappingEncoder

public class LayoutWrappingEncoder<E> extends 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

Logback 相关组件

看下其继承结构

Logback 相关组件

LayoutBase

Logback 相关组件

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;

这里出现另一个组件 Converter

PatternLayout

Logback 相关组件

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

Logback 相关组件

Logback 相关组件

我们直接看下 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 获取对应的属性值

本文由mdnice多平台发布