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");
}
}
对配置文件的节点解释
我们看看 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 关联上的?
Appender
看看它的继承结构
UnsynchronizedAppenderBase
直接看看 doAppend 方法
看到通过 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
继承结构
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
看下其继承结构
LayoutBase
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
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
我们直接看下 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多平台发布
转载自:https://juejin.cn/post/7160286269161865252