首页 > 编程语言 >slf4j+logback源码加载流程解析

slf4j+logback源码加载流程解析

时间:2024-01-07 12:33:06浏览次数:44  
标签:environment null initializationContext slf4j 源码 new logFile logback


slf4j绑定logback源码解析

Logger log = LoggerFactory.getLogger(LogbackDemo.class);

如上述代码所示,在项目中通常会这样创建一个Logger对象去打印日志。
然后点进去,会走到LoggerFactory的getILoggerFactory()方法,如下代码所示。

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()方法,如下代码所示。

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();

slf4j+logback源码加载流程解析_xml


StaticLoggerBinder类加载时会执行初始化,如下代码所示。

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()方法源码如下:

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()方法源码如下。

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()方法。

slf4j+logback源码加载流程解析_xml_02

slf4j+logback源码加载流程解析_xml_03


因此,最终返回出去的是LogbackLoggingSystem对象,然后执行loggingSystem.beforeInitialize()方法,beforeInitialize会使用到logback的StaticLoggerBinder类,因此会读取logback.xml完成logback的初始化。

第二个事件是ApplicationEnvironmentPreparedEvent,会执行日志系统的初始化。

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


标签:environment,null,initializationContext,slf4j,源码,new,logFile,logback
From: https://blog.51cto.com/u_14643435/9133847

相关文章

  • 医学检验科LIS系统,LIS检验系统源码
    LIS系统功能模块字典模块:系统参数、标本管理、试管管理、平台设备管理、送检类型管理、检验项目管理、检查组合管理、项目转换管理。报告模块:试管条码打印、检验报告管理、报告登记、报告接收、报告打印、历史数据查询、数据存根、报告审核。质控模块:质控品管理、质控规则管理......
  • Java智慧工地可视化APP信息管理平台源码
    智慧工地信息化解决方案、智慧工地信息管理平台智慧工地系统以推进施工过程管理信息化、数字化、智慧化为手段,促进第五代通信技术(5G)、大数据、智能设备、人工智能等与建筑工程管理进一步融合。智慧化工地建设全面加速,以数字技术助力建筑工地转型升级、提速增效、提档升级的成......
  • RocketMQ系统性学习-RocketMQ原理分析之源码启动、Broker启动流程分析
    欢迎关注公众号:【11来了】发送“资料”可以下载Redis、JVM系列文章PDF版本!作者为在读研究生,目前研二,计划在公众号记录学习常用中间件笔记,以及明年更新面试经历!RocketMQ原理分析启动RocketMQ源码分析RocketMQ之前,先确保可以成功启动起来NameServer启动在Idea中配置ROCK......
  • 智慧工地智能化管理平台源码
    智慧工地是一种基于信息技术和大数据应用的智能化管理平台,旨在提升建筑施工现场的安全、效率和质量。通过物联网、云计算、人工智能等技术手段,智慧工地可以对施工现场的各个要素进行全面感知、实时交互和智能分析,以实现更高效、更安全、更环保的施工过程。 技术架构:微服务架构+Jav......
  • PHP语言B/S架构的医院不良事件报告系统源码
    医院安全(不良)事件管理系统采用无责的、自愿的填报不良事件方式,有效地减轻医护人员的思想压力,实现以事件为主要对象,可以自动、及时、实际地反应医院的安全、不良、近失事件的情况,更好地掌握不良事件的发生趋势,未及时采取适当的管理措施和流程、制度改进提供了良好的量化依据。系统通......
  • 点餐系统源码(小程序+APP+H5)-外卖-点餐-餐饮
     PHP点餐系统是餐营业管理的“机械”部分。它们是获取我们的预测、实际订单、安全库存和订单数量并将其转换为采购订单或生产订单的程序。由于其机械性质,订购系统并没有太多理论。但这并不意味着您不需要了解一些事情。PHP点餐系统是一种基于Web的应用程序,旨在帮助餐厅和餐馆管......
  • 安卓期末小项目TrackTable收支表+源码
    一、需求分析这是一款账目记录、分析App,本系统主要功能有:用户登录注册、首页账单分析、上传账单、搜索账单信息、个人信息、重置密码、数据效验。系统功能图系统总用例图二、系统开发平台环境IDE:AndroidStudio 2021.1.x插件:simpleUMLCE工具:Visustinv8DemoJava版本:Java11OS:win11......
  • 【Redis深度专题】「核心技术提升」从源码角度探究Redis服务的内存使用、清理以及逐出
    背景介绍Redis作为一种高性能的内存NoSQL数据库,其容量受限于最大内存的限制。用户在使用阿里云Redis时,除了对性能和稳定性有较高的要求外,对内存占用也非常敏感。然而,在实际使用中,一些用户可能会发现他们的线上实例的内存占用比预期的要大。内存较高的场景在使用Redis时,以下是一些可......
  • 基于SpringBoot+Vue的线上课程管理系统设计实现(源码+lw+部署文档+讲解等)
    (文章目录)前言:heartpulse:博主介绍:✌全网粉丝10W+,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌:heartpulse:......
  • springboot057洗衣店订单管理系统-计算机毕业设计源码+LW文档
    论文选题理由衣服是人们必不可少的重要物品,它让人们显得好看,变得舒适。一些人也会花费很昂贵的价格去购买自己心意的衣服,当我们刚刚购买时衣服非常的整洁非常的漂亮,但是当我们穿了一段时间后烦恼就来了。衣服变脏了、粘上油渍了,或者是放了一段时间后发黄了。这样的问题很多,而且很......