logback的配置文件加载过程还是很简单的,这里做一下简单记录
1、maven
logback-classic已经包含了logback-core和slf4j的依赖,不需要额外引入了
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.10</version>
</dependency>
2、入口
平时我们使用 logback入口都是 LoggerFactory.getLogger() 这个 api,我们debug就从这里进入
public static Logger getLogger(String name) {
ILoggerFactory iLoggerFactory = getILoggerFactory();
return iLoggerFactory.getLogger(name);
}
进入 getILoggerFactory()方法
这个方法里面使用了一堆常量,这里简单介绍
常量 | 含义 |
---|---|
UNINITIALIZED | 还未初始化,需要初始化日志环境 |
SUCCESSFUL_INITIALIZATION | 成功初始化,可以执行获取日志对象 |
NOP_FALLBACK_INITIALIZATION | 已经初始化了,但是没有只有门面,没有实现,所以会返回 NOPLoggerFactory ,这个工厂中创建的日志对象什么都不处理,也就是打印不出来日志 |
FAILED_INITIALIZATION | 初始化失败,无法使用,会报错 |
ONGOING_INITIALIZATION | 正在初始化,表明多线程环境下,第一个线程使用已经开始初始化了,但是还没有初始化完成,第二个线程又进来这时候就会返回一个SubstituteLoggerFactory,临时使用这个日志工厂创建日志对象 |
INITIALIZATION_STATE | 当前的slf4j的状态,默认为UNINITIALIZED |
public static ILoggerFactory getILoggerFactory() {
if (INITIALIZATION_STATE == UNINITIALIZED) {
synchronized (LoggerFactory.class) {
if (INITIALIZATION_STATE == UNINITIALIZED) {
INITIALIZATION_STATE = ONGOING_INITIALIZATION;
performInitialization();
}
}
}
switch (INITIALIZATION_STATE) {
case SUCCESSFUL_INITIALIZATION:
return StaticLoggerBinder.getSingleton().getLoggerFactory();
case NOP_FALLBACK_INITIALIZATION:
return NOP_FALLBACK_FACTORY;
case FAILED_INITIALIZATION:
throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG);
case ONGOING_INITIALIZATION:
// support re-entrant behavior.
// See also http://jira.qos.ch/browse/SLF4J-97
return SUBST_FACTORY;
}
throw new IllegalStateException(\"Unreachable code\");
}
详细的流程这里不说了,因为我们要看的是日志的配置文件加载过程,所以此时第一次进入肯定是未初始化UNINITIALIZED,只需要看performInitialization方法即可,我们进入
private final static void performInitialization() {
bind();
if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) {
versionSanityCheck();
}
}
绑定完成后,会将当前状态修改为初始化完成,我们进入 bind方法
private final static void bind() {
try {
Set<URL> staticLoggerBinderPathSet = null;
if (!isAndroid()) {
staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
}
// 日志绑定对象
StaticLoggerBinder.getSingleton();
INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
reportActualBinding(staticLoggerBinderPathSet);
} catch (NoClassDefFoundError ncde) {
//省略异常处理流程
}
}
StaticLoggerBinder是个单例,它初始化的过程,不仅决定使用那个实现,还决定配置文件的加载,我们进入getSingleton方法
private static StaticLoggerBinder SINGLETON = new StaticLoggerBinder();
static {
SINGLETON.init();
}
public static StaticLoggerBinder getSingleton() {
return SINGLETON;
}
可以看到StaticLoggerBinder在实例化过程中,会调用init方法,我们进入
void init() {
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);
initialized = true;
} catch (Exception t) { // see LOGBACK-1159
Util.report(\"Failed to instantiate [\" + LoggerContext.class.getName() + \"]\", t);
}
}
这里面紧急执行了 autoConfig方法,我们进入
public void autoConfig() throws JoranException {
StatusListenerConfigHelper.installIfAsked(loggerContext);
//获取默认的配置文件url,源码可见2.1
URL url = findURLOfDefaultConfigurationFile(true);
// 如果获取到则进行配置
if (url != null) {
configureByResource(url);
}
// 没有获取到默认的配置文件url,则继续判断
else {
// 通过jdk的spi机制获取配置文件对象,源码可见2.2
Configurator c = EnvUtil.loadFromServiceLoader(Configurator.class);
// 获取到配置文件对象,则对loggerContext进行配置
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);
}
}
// 未获取到配置文件对象,则使用BasicConfigurator进行配置,
else {
BasicConfigurator basicConfigurator = new BasicConfigurator();
basicConfigurator.setContext(loggerContext);
basicConfigurator.configure(loggerContext);
}
}
}
2.1、获取默认的配置文件url
public URL findURLOfDefaultConfigurationFile(boolean updateStatus) {
// 获取配置文件
ClassLoader myClassLoader = Loader.getClassLoaderOfObject(this);
// 获取系统变量中指定的名为,logback.configurationFile,的配置文件
URL url = findConfigFileURLFromSystemProperties(myClassLoader, updateStatus);
if (url != null) {
return url;
}
// 获取名为logback-test.xml的配置文件
url = getResource(TEST_AUTOCONFIG_FILE, myClassLoader, updateStatus);
if (url != null) {
return url;
}
// 获取名为logback.xml的配置文件
return getResource(AUTOCONFIG_FILE, myClassLoader, updateStatus);
}
2.2、通过java spi 获取配置对象
public static <T> T loadFromServiceLoader(Class<T> c) {
ServiceLoader<T> loader = ServiceLoader.load(c, getServiceLoaderClassLoader());
Iterator<T> it = loader.iterator();
if (it.hasNext())
return it.next();
return null;
}
3、总结
在第一次获取Logger对象时,会加载配置文件,顺序是
- 系统变量中的名为 logback.configurationFile的配置文件
- 类路径下的名为 logback-test.xml的配置文件
- 类路径下的名为 logback-test.xml的配置文件
- java spi 机制实现了ch.qos.logback.classic.spi.Configurator接口的类
- 默认的配置类BasicConfigurator
来源:https://www.cnblogs.com/junzisi/p/16182574.html
本站部分图文来源于网络,如有侵权请联系删除。