前面介绍了slf4j相关的知识点, 提到了实现模块是通过SLF4JServiceProvider
进行自定义日志框架的, 本节就来介绍logback的LogbackServiceProvider
public class LogbackServiceProvider implements SLF4JServiceProvider {
public void initialize() {
// 日志上下文
defaultLoggerContext = new LoggerContext();
// 设置名称:default
defaultLoggerContext.setName(CoreConstants.DEFAULT_CONTEXT_NAME);
// 初始化日志上下文
initializeLoggerContext();
// 标记上下文已启动, 顺便发送启动事件
defaultLoggerContext.start();
markerFactory = new BasicMarkerFactory();
mdcAdapter = new LogbackMDCAdapter();
// set the MDCAdapter for the defaultLoggerContext immediately
defaultLoggerContext.setMDCAdapter(mdcAdapter);
}
}
LoggerContext
, 默认名字为default核心的部分是初始化日志上下文initializeLoggerContext
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);
}
}
// ContextInitializer#autoConfig
public void autoConfig(ClassLoader classLoader) throws JoranException {
// see https://github.com/qos-ch/logback/issues/715
classLoader = Loader.systemClassloaderIfNull(classLoader);
// 检查logback-classic的版本
checkVersions();
// 添加日志状态监听器
StatusListenerConfigHelper.installIfAsked(loggerContext);
// invoke custom configurators
// spi获取Configurator
List<Configurator> configuratorList = ClassicEnvUtil.loadFromServiceLoader(Configurator.class, classLoader);
// 排个序
configuratorList.sort(rankComparator);
if (configuratorList.isEmpty()) {
contextAware.addInfo("No custom configurators were discovered as a service.");
} else {
// 打印获取到的configurator
printConfiguratorOrder(configuratorList);
}
// 执行spi获取的Configurator
for (Configurator c : configuratorList) {
// 返回执行状态为DO_NOT_INVOKE_NEXT_IF_ANY直接返回
if (invokeConfigure(c) == Configurator.ExecutionStatus.DO_NOT_INVOKE_NEXT_IF_ANY) {
return;
}
}
// 执行默认的Configurator
// 1. SerializedModelConfigurator
// 2. DefaultJoranConfigurator: 解析配置文件
// 3. BasicConfigurator
for (String configuratorClassName : INTERNAL_CONFIGURATOR_CLASSNAME_LIST) {
contextAware.addInfo("Trying to configure with "+configuratorClassName);
// 实例化
Configurator c = instantiateConfiguratorByClassName(configuratorClassName, classLoader);
if (c == null) {
continue;
}
// 解析配置文件
if (invokeConfigure(c) == Configurator.ExecutionStatus.DO_NOT_INVOKE_NEXT_IF_ANY) {
return;
}
}
}
方法小结
初始化逻辑不属于LogbackServiceProvider
, 内容又比较长怎么办, logback就创建了一个上下文初始化对象ContextInitializer
来帮助处理启动初始化这个动作
这里的核心Configurator是DefaultJoranConfigurator, 用来解析我们常用的logback.xml
@ConfiguratorRank(value = ConfiguratorRank.NOMINAL)
public class DefaultJoranConfigurator extends ContextAwareBase implements Configurator {
@Override
public ExecutionStatus configure(LoggerContext context) {
// 查找配置文件的路径
URL url = performMultiStepConfigurationFileSearch(true);
if (url != null) {
try {
// 解析url
configureByResource(url);
} catch (JoranException e) {
e.printStackTrace();
}
// You tried and that counts Mary.
// 解析正常不再继续后续的Configurator解析
return ExecutionStatus.DO_NOT_INVOKE_NEXT_IF_ANY;
} else {
// 异常执行下一个
return ExecutionStatus.INVOKE_NEXT_IF_ANY;
}
}
}
这里有两个重要的方法
performMultiStepConfigurationFileSearch
private URL performMultiStepConfigurationFileSearch(boolean updateStatus) {
ClassLoader myClassLoader = Loader.getClassLoaderOfObject(this);
// 获取系统指定的配置文件地址; key:logback.configurationFile
URL url = findConfigFileURLFromSystemProperties(myClassLoader, updateStatus);
if (url != null) {
return url;
}
// logback-test.xml
url = getResource(ClassicConstants.TEST_AUTOCONFIG_FILE, myClassLoader, updateStatus);
if (url != null) {
return url;
}
// logback.xml
return getResource(ClassicConstants.AUTOCONFIG_FILE, myClassLoader, updateStatus);
}
// 获取指定的日志文件地址
private URL findConfigFileURLFromSystemProperties(ClassLoader classLoader, boolean updateStatus) {
// 系统属性获取 key:logback.configurationFile
String logbackConfigFile = OptionHelper.getSystemProperty(ClassicConstants.CONFIG_FILE_PROPERTY);
if (logbackConfigFile != null) {
URL result = null;
try {
// 1.网路路径
result = new URL(logbackConfigFile);
return result;
} catch (MalformedURLException e) {
// 2.classpath路径
result = Loader.getResource(logbackConfigFile, classLoader);
if (result != null) {
return result;
}
// 3.磁盘文件路径
File f = new File(logbackConfigFile);
if (f.exists() && f.isFile()) {
try {
result = f.toURI().toURL();
return result;
} catch (MalformedURLException e1) {
}
}
} finally {
// 打印日志
if (updateStatus) {
statusOnResourceSearch(logbackConfigFile, classLoader, result);
}
}
}
return null;
}
方法小结
-Dlogback.configurationFile=你的log配置文件地址
指定需要解析的配置文件; 它可以是网络路径, 也可以是磁盘路径, 也可以是项目中classpath下的路径logback-test.xml
或者 logback.xml
, 顺序是以logback-test.xml
优先哦, 如果项目中这两个都配置了, 那么只有logback-test.xml
会生效classpath路径指的是我们项目中编译后, 源码同级及以下的路径, 例如我们maven项目中配置的Sources和Resources
configureByResource
public void configureByResource(URL url) throws JoranException {
if (url == null) {
throw new IllegalArgumentException("URL argument cannot be null");
}
final String urlString = url.toString();
// 一般是logback.xml
if (urlString.endsWith("xml")) {
JoranConfigurator configurator = new JoranConfigurator();
configurator.setContext(context);
configurator.doConfigure(url);
} else {
throw new LogbackException(
"Unexpected filename extension of file [" + url.toString() + "]. Should be .xml");
}
}
使用JoranConfigurator解析xml文件, 看得出来仅支持xml文件的日志配置
继承图
JoranConfigurator是logback中最重要的一个配置器类, 它包含了
具体的解析流量将会在下一篇中介绍到
Configurator
进行初始化, 如果存在(默认不存在), 并且执行后的返回值为DO_NOT_INVOKE_NEXT_IF_ANY
则解析完成Configurator
进行初始化, 其中DefaultJoranConfigurator
比较核心, 是logback用来解析logback.xml的配置类, 解析成功后会返回DO_NOT_INVOKE_NEXT_IF_ANY
, 直接终端后面的配置类解析; 默认的三个配置类如下-Dlogback.configurationFile
指定使用的logback.xml文件的位置, 可以是网络路径, 磁盘路径, classpath路径; 所以不一定是logback.xml这个名字(后缀必须是xml), 也不定需要放在classpath目录下logback-test.xml
, 如果没有, 则会获取classpath下的logback.xml
JoranConfigurator
类解析获取到的配置文件, 例如 logback.xmlConfigurator
类, 然后自己加载配置文件进行解析