百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

LogBack在SpringBoot的启动逻辑(logback springprofile)

lipiwang 2024-11-15 22:02 26 浏览 0 评论

引言

在SpringBoot项目中,只要增加了logback.xml 配置文件,日志就会按配置好的方式,进行日志的输出,那日志的功能是如何配置到项目中的呢?

分析

日志启动依赖于spring容器中的监听器机制,LoggingApplicationListener类实现了该功能

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

LoggingApplicationListener实现了对 ApplicationStartingEvent、ApplicationEnvironmentPreparedEvent、ApplicationPreparedEvent的监听。

ApplicationStartingEvent:负责初始化。

ApplicationEnvironmentPreparedEvent: 负责监听配置变更的处理,当有配置变更时,会进行重新加载。

初始化

private void onApplicationStartingEvent(ApplicationStartingEvent event) {
// 确认当前使用种日志框架,按以下顺序逐个尝试
//systems.put("ch.qos.logback.core.Appender", "org.springframework.boot.logging.logback.LogbackLoggingSystem");
//	systems.put("org.apache.logging.log4j.core.impl.Log4jContextFactory",
//			"org.springframework.boot.logging.log4j2.Log4J2LoggingSystem");
//	systems.put("java.util.logging.LogManager", "org.springframework.boot.logging.java.JavaLoggingSystem");
this.loggingSystem = LoggingSystem.get(event.getSpringApplication().getClassLoader());
// 初始化前的处理
this.loggingSystem.beforeInitialize();
}

该方法只是初始化当前的LoggingSystem对象,同时会按注释中的顺序进行查看当前要使用哪个LoggingSystem的子类,是否在当前环境中有该类,因为现在以logback来分析,所以 关注LogbackLoggingSystem即可。

环境配置准备

当environment中的数据准备好后,就可以开始进行处理log框架的初始化内容。

private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
if (this.loggingSystem == null) {
    this.loggingSystem = LoggingSystem.get(event.getSpringApplication().getClassLoader());
}
initialize(event.getEnvironment(), event.getSpringApplication().getClassLoader());
}
protected void initialize(ConfigurableEnvironment environment, ClassLoader classLoader) {
    // 应用配置中以logging.开头的配置
    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);
    // 初始化logSystem,这里是重点
    initializeSystem(environment, this.loggingSystem, this.logFile);
    // 初始化logging level,从环境变量中获取,配置文件中同名的logger level会覆盖配置中的
    initializeFinalLoggingLevels(environment, this.loggingSystem);
    registerShutdownHookIfNecessary(environment, this.loggingSystem);
}

读取配置文件

	@Override
	public void initialize(LoggingInitializationContext initializationContext, String configLocation, LogFile logFile) {
		// 单例的LoggerContext
        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.");
		}
	}
	public void initialize(LoggingInitializationContext initializationContext, String configLocation, LogFile logFile) {
		if (StringUtils.hasLength(configLocation)) {
			initializeWithSpecificConfig(initializationContext, configLocation, logFile);
			return;
		}
        // 初始化
		initializeWithConventions(initializationContext, logFile);
	}
	private void initializeWithConventions(LoggingInitializationContext initializationContext, LogFile logFile) {
		// 尝试标准的日志配置文件,如   "logback-test.groovy", "logback-test.xml", "logback.groovy", "logback.xml"
        String config = getSelfInitializationConfig();
		if (config != null && logFile == null) { // 如果存在,则自身进行初始化
			// self initialization has occurred, reinitialize in case of property changes
			reinitialize(initializationContext);
			return;
		}
        // 如果没有找到标准的,则使用spring-开头的标准文件 "logback-test.groovy", "logback-test.xml", "logback.groovy", "logback.xml"
		if (config == null) {
			config = getSpringInitializationConfig();
		}
		if (config != null) {
			loadConfiguration(initializationContext, config, logFile);
			return;
		}
		loadDefaults(initializationContext, logFile);
	}
	protected void reinitialize(LoggingInitializationContext initializationContext) {
		getLoggerContext().reset();
		getLoggerContext().getStatusManager().clear();
        // 加载配置文件 
		loadConfiguration(initializationContext, getSelfInitializationConfig(), null);
	}
	protected void loadConfiguration(LoggingInitializationContext initializationContext, String location,
			LogFile logFile) {
		super.loadConfiguration(initializationContext, location, logFile);
		LoggerContext loggerContext = getLoggerContext();
		stopAndReset(loggerContext);
		try {
            //从配置文件中进行配置
			configureByResourceUrl(initializationContext, loggerContext, ResourceUtils.getURL(location));
		}
		catch (Exception ex) {
			throw new IllegalStateException("Could not initialize Logback logging from " + location, ex);
		}
		List<Status> statuses = loggerContext.getStatusManager().getCopyOfStatusList();
		StringBuilder errors = new StringBuilder();
		for (Status status : statuses) {
			if (status.getLevel() == Status.ERROR) {
				errors.append((errors.length() > 0) ? String.format("%n") : "");
				errors.append(status.toString());
			}
		}
		if (errors.length() > 0) {
			throw new IllegalStateException(String.format("Logback configuration error detected: %n%s", errors));
		}
	}

配置日志级别

	private void initializeFinalLoggingLevels(ConfigurableEnvironment environment, LoggingSystem system) {
		// logGroup 是定义一系列的Loggername为一个分组,后续可以对当前这个分组进行配置loggerLevel
        // 这样,在相同组下的所有logger的配置日志级别都会被配置
        bindLoggerGroups(environment);
		if (this.springBootLogging != null) {
			initializeLogLevel(system, this.springBootLogging);
		}
        // 配置级别
        // springboot中的yml的配置,会覆盖logback.xml中的配置
		setLogLevels(system, environment);
	}

应用准备

private void onApplicationPreparedEvent(ApplicationPreparedEvent event) {
    // 将LogggingSystem注册到Spring容器中
    ConfigurableListableBeanFactory beanFactory = event.getApplicationContext().getBeanFactory();
    if (!beanFactory.containsBean(LOGGING_SYSTEM_BEAN_NAME)) {
        beanFactory.registerSingleton(LOGGING_SYSTEM_BEAN_NAME, this.loggingSystem);
    }
    if (this.logFile != null && !beanFactory.containsBean(LOG_FILE_BEAN_NAME)) {
        beanFactory.registerSingleton(LOG_FILE_BEAN_NAME, this.logFile);
    }
    // 注册LoggerGroups的bean
    if (this.loggerGroups != null && !beanFactory.containsBean(LOGGER_GROUPS_BEAN_NAME)) {
        beanFactory.registerSingleton(LOGGER_GROUPS_BEAN_NAME, this.loggerGroups);
    }
}

在上面会注册三个Bean,在Spring容器中可以获取出来,进行调整对应的配置

demo配置示例

示例代码

@RestController
public class LoggerController {

    Logger logger = LoggerFactory.getLogger(LoggerController.class);
    @Autowired
    @Qualifier(LOGGER_GROUPS_BEAN_NAME)
    LoggerGroups loggerGroups;
    @Autowired
    @Qualifier(LOGGING_SYSTEM_BEAN_NAME)
    LoggingSystem loggingSystem;

    /**
     * 获取当前分组
     * @return
     */
    @RequestMapping("/api/logger-groups")
    public LoggerGroups getLoggerGroups() {
        return loggerGroups;
    }

    /**
     * 设置logger级别
     * @param loggerName
     * @param level
     */
    @RequestMapping(value = "/api/set-logger-level")
    public void getLogLevel(@RequestParam("name") String loggerName, @RequestParam("level") LogLevel level) {
        this.loggingSystem.setLogLevel(loggerName, level);
    }

    /**
     * 设置分组级别
     * @param group
     * @param level
     */
    @RequestMapping(value = "/api/set-group-level")
    public void updateLoggerGroup(@RequestParam("group") String group,@RequestParam("level") LogLevel level) {
        this.loggerGroups.get(group).configureLogLevel(level,(k,l) ->
                this.loggingSystem.setLogLevel(k,l)
                );
    }

    @Scheduled(fixedRate = 2000)
    public void run() {
        logger.debug("debug...");
        logger.info("info...");
        logger.warn("warn...");
        logger.error("error...");
    }
}

配置

logging:
  group:
    demo:
      - org.example.LoggerController
      - org.example.TestController

测试

测试分组

http://localhost:8080/api/set-group-level?group=demo&level=WARN

测试指定loggername

localhost:8080/api/set-logger-level?name=org.example.TestController&level=DEBUG

相关推荐

前端入门——css 网格轨道详细介绍

上篇前端入门——cssGrid网格基础知识整体大概介绍了cssgrid的基本概念及使用方法,本文将介绍创建网格容器时会发生什么?以及在网格容器上使用行、列属性如何定位元素。在本文中,将介绍:...

Islands Architecture(孤岛架构)在携程新版首页的实践

一、项目背景2022,携程PC版首页终于迎来了首次改版,完成了用户体验与技术栈的全面升级。作为与用户连接的重要入口,旧版PC首页已经陪伴携程走过了22年,承担着重要使命的同时,也遇到了很多问题:维护/...

HTML中script标签中的那些属性

HTML中的<script>标签详解在HTML中,<script>标签用于包含或引用JavaScript代码,是前端开发中不可或缺的一部分。通过合理使用<scrip...

CSS 中各种居中你真的玩明白了么

页面布局中最常见的需求就是元素或者文字居中了,但是根据场景的不同,居中也有简单到复杂各种不同的实现方式,本篇就带大家一起了解下,各种场景下,该如何使用CSS实现居中前言页面布局中最常见的需求就是元...

CSS样式更改——列表、表格和轮廓

上篇文章主要介绍了CSS样式更改篇中的字体设置Font&边框Border设置,这篇文章分享列表、表格和轮廓,一起来看看吧。1.列表List1).列表的类型<ulstyle='list-...

一文吃透 CSS Flex 布局

原文链接:一文吃透CSSFlex布局教学游戏这里有两个小游戏,可用来练习flex布局。塔防游戏送小青蛙回家Flexbox概述Flexbox布局也叫Flex布局,弹性盒子布局。它决定了...

css实现多行文本的展开收起

背景在我们写需求时可能会遇到类似于这样的多行文本展开与收起的场景:那么,如何通过纯css实现这样的效果呢?实现的难点(1)位于多行文本右下角的展开收起按钮。(2)展开和收起两种状态的切换。(3)文本...

css 垂直居中的几种实现方式

前言设计是带有主观色彩的,同样网页设计中的css一样让人摸不头脑。网上列举的实现方式一大把,或许在这里你都看到过,但既然来到这里我希望这篇能让你看有所收获,毕竟这也是前端面试的基础。实现方式备注:...

WordPress固定链接设置

WordPress设置里的最后一项就是固定链接设置,固定链接设置是决定WordPress文章及静态页面URL的重要步骤,从站点的SEO角度来讲也是。固定链接设置决定网站URL,当页面数少的时候,可以一...

面试发愁!吃透 20 道 CSS 核心题,大厂 Offer 轻松拿

前端小伙伴们,是不是一想到面试里的CSS布局题就发愁?写代码时布局总是对不齐,面试官追问兼容性就卡壳,想跳槽却总被“多列等高”“响应式布局”这些问题难住——别担心!从今天起,咱们每天拆解一...

3种CSS清除浮动的方法

今天这篇文章给大家介绍3种CSS清除浮动的方法。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。首先,这里就不讲为什么我们要清楚浮动,反正不清除浮动事多多。下面我就讲3种常用清除浮动的...

2025 年 CSS 终于要支持强大的自定义函数了?

大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发!1.什么是CSS自定义属性CSS自...

css3属性(transform)的一个css3动画小应用

闲言碎语不多讲,咱们说说css3的transform属性:先上效果:效果说明:当鼠标移到a标签的时候,从右上角滑出二维码。实现方法:HTML代码如下:需要说明的一点是,a链接的跳转需要用javasc...

CSS基础知识(七)CSS背景

一、CSS背景属性1.背景颜色(background-color)属性值:transparent(透明的)或color(颜色)2.背景图片(background-image)属性值:none(没有)...

CSS 水平居中方式二

<divid="parent"><!--定义子级元素--><divid="child">居中布局</div>...

取消回复欢迎 发表评论: