slf4j绑定logback源码解析
java
Logger log = LoggerFactory.getLogger(LogbackDemo.class);
如上述代码所示,在项目中通常会这样创建一个Logger对象去打印日志。
然后点进去,会走到LoggerFactory的getILoggerFactory()方法,如下代码所示。
java
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");
}
performInitialization()方法表示执行初始化,点进去会调用到LoggerFactory的bind()方法,如下代码所示。
java
Set<URL> staticLoggerBinderPathSet = null;
if (!isAndroid()) {
// 查找org/slf4j/impl/StaticLoggerBinder.class这个类的路径
staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
// 如果有多个则打印Class path contains multiple SLF4J bindings
reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
}
// StaticLoggerBinder类是各个日志框架提供的,比如logback,如下图所示
StaticLoggerBinder.getSingleton();
INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
reportActualBinding(staticLoggerBinderPathSet);
fixSubstituteLoggers();
replayEvents();
// release all resources in SUBST_FACTORY
SUBST_FACTORY.clear();
StaticLoggerBinder类加载时会执行初始化,如下代码所示。
java
static {
SINGLETON.init();
}
void init() {
try {
try {
// 这里会完成logback的自动配置
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);
}
}
logback源码解析
ContextInitializer.autoConfig()方法源码如下:
java
public void autoConfig() throws JoranException {
StatusListenerConfigHelper.installIfAsked(loggerContext);
// 查找配置文件,优先级:系统属性logback.configurationFile > logback-test.xml > logback.xml
URL url = findURLOfDefaultConfigurationFile(true);
if (url != null) {
configureByResource(url);
} else {
// 使用java的spi机制,查找Configurator的实现,如果有,则自动配置logback
Configurator c = EnvUtil.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来自动配置logback
BasicConfigurator basicConfigurator = new BasicConfigurator();
basicConfigurator.setContext(loggerContext);
basicConfigurator.configure(loggerContext);
}
}
}
spring boot初始化logback流程
Spring Boot启动的时候,由org.springframework.boot.context.logging.LoggingApplicationListener根据情况初始化并使用。
在spring 初始化启动的过程中,会根据生命周期的不同阶段,发出对应的动作。这就是Spring ApplicationListener,设计基于观察者模式,而其中LoggingApplicationListener类便是负责logging日志框架的初始化操作。
LoggingApplicationListener被配置在spring-boot-x.x.x.jar的spring.factories文件中,spring启动的时候会去读取这个文件。
LoggingApplicationListener.onApplicationEvent()方法源码如下。
java
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationStartingEvent) {
onApplicationStartingEvent((ApplicationStartingEvent) event);
}
else if (event instanceof ApplicationEnvironmentPreparedEvent) {
onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
}
else if (event instanceof ApplicationPreparedEvent) {
onApplicationPreparedEvent((ApplicationPreparedEvent) event);
}
else if (event instanceof ContextClosedEvent
&& ((ContextClosedEvent) event).getApplicationContext().getParent() == null) {
onContextClosedEvent();
}
else if (event instanceof ApplicationFailedEvent) {
onApplicationFailedEvent();
}
}
第一个事件就是ApplicationStartingEvent了,点进去到LoggingSystem.get()方法。
因此,最终返回出去的是LogbackLoggingSystem对象,然后执行loggingSystem.beforeInitialize()方法,beforeInitialize会使用到logback的StaticLoggerBinder类,因此会读取logback.xml完成logback的初始化。
第二个事件是ApplicationEnvironmentPreparedEvent,会执行日志系统的初始化。
java
protected void initialize(ConfigurableEnvironment environment, ClassLoader classLoader) {
new LoggingSystemProperties(environment).apply();
this.logFile = LogFile.get(environment);
if (this.logFile != null) {
this.logFile.applyToSystemProperties();
}
this.loggerGroups = new LoggerGroups(DEFAULT_GROUP_LOGGERS);
initializeEarlyLoggingLevel(environment);
initializeSystem(environment, this.loggingSystem, this.logFile);
initializeFinalLoggingLevels(environment, this.loggingSystem);
registerShutdownHookIfNecessary(environment, this.loggingSystem);
}
// 主要看initializeSystem方法
private void initializeSystem(ConfigurableEnvironment environment, LoggingSystem system, LogFile logFile) {
LoggingInitializationContext initializationContext = new LoggingInitializationContext(environment);
// 如果application.yml里指定了logging.config,则使用指定的文件初始化,否则按默认处理
String logConfig = environment.getProperty(CONFIG_PROPERTY);
if (ignoreLogConfig(logConfig)) {
system.initialize(initializationContext, null, logFile);
}
else {
try {
ResourceUtils.getURL(logConfig).openStream().close();
system.initialize(initializationContext, logConfig, logFile);
}
catch (Exception ex) {
// NOTE: We can't use the logger here to report the problem
System.err.println("Logging system failed to initialize using configuration from '" + logConfig + "'");
ex.printStackTrace(System.err);
throw new IllegalStateException(ex);
}
}
}
// 下面会走到LogbackLoggingSystem.initialize()方法
public void initialize(LoggingInitializationContext initializationContext, String configLocation, LogFile logFile) {
LoggerContext loggerContext = getLoggerContext();
if (isAlreadyInitialized(loggerContext)) {
return;
}
super.initialize(initializationContext, configLocation, logFile);
loggerContext.getTurboFilterList().remove(FILTER);
markAsInitialized(loggerContext);
if (StringUtils.hasText(System.getProperty(CONFIGURATION_FILE_PROPERTY))) {
getLogger(LogbackLoggingSystem.class.getName()).warn("Ignoring '" + CONFIGURATION_FILE_PROPERTY
+ "' system property. Please use 'logging.config' instead.");
}
}
// 再到AbstractLoggingSystem.initialize()方法
public void initialize(LoggingInitializationContext initializationContext, String configLocation, LogFile logFile) {
if (StringUtils.hasLength(configLocation)) {
initializeWithSpecificConfig(initializationContext, configLocation, logFile);
return;
}
initializeWithConventions(initializationContext, logFile);
}
// 再到initializeWithConventions方法
private void initializeWithConventions(LoggingInitializationContext initializationContext, LogFile logFile) {
// 查找logback自己的配置文件logback-test.xml、logback.xml
String config = getSelfInitializationConfig();
if (config != null && logFile == null) {
// logback在前面第一次使用StaticLoggerBinder时已经完成了初始化,这里会重新初始化一次
reinitialize(initializationContext);
return;
}
if (config == null) {
// 获取logback-spring.xml
config = getSpringInitializationConfig();
}
if (config != null) {
// 加载配置logback-spring.xml
loadConfiguration(initializationContext, config, logFile);
return;
}
// 加载默认配置
loadDefaults(initializationContext, logFile);
}
// reinitialize方法会走到LogbackLoggingSystem.configureByResourceUrl()方法,这里使用了SpringBootJoranConfigurator
private void configureByResourceUrl(LoggingInitializationContext initializationContext, LoggerContext loggerContext,
URL url) throws JoranException {
if (url.toString().endsWith("xml")) {
JoranConfigurator configurator = new SpringBootJoranConfigurator(initializationContext);
configurator.setContext(loggerContext);
configurator.doConfigure(url);
}
else {
// 否则使用logback的ContextInitializer进行初始化,这就不支持springProperty标签
new ContextInitializer(loggerContext).configureByResource(url);
}
}
// SpringBootJoranConfigurator添加了额外的规则,如springProperty标签
class SpringBootJoranConfigurator extends JoranConfigurator {
@Override
public void addInstanceRules(RuleStore rs) {
super.addInstanceRules(rs);
Environment environment = this.initializationContext.getEnvironment();
rs.addRule(new ElementSelector("configuration/springProperty"), new SpringPropertyAction(environment));
rs.addRule(new ElementSelector("*/springProfile"), new SpringProfileAction(environment));
rs.addRule(new ElementSelector("*/springProfile/*"), new NOPAction());
}
}