springboot整合日志系统

环境:jdk 1.8,spring-boot-autoconfigure-1.5.17.RELEASE.jar,slf4j-api-1.7.25.jar
查看springboot-autoconfigure中的日志模块logging,提供两个类:AutoConfigurationReportLoggingInitializer、ConditionEvaluationReportMessage。从类名中可以看到是一个报告类。查看源码确实如此。那么也就是springboot的自动配置类中没有操作什么。
查看springboot中的日志模块logging,其中提供两个应用监听器:ClasspathLoggingApplicationListener、LoggingApplicationListener。了解springboot启动流程的同学应该知道springboot通过spi注册了许多的监听器,查看spring.factories中发现其中就包含刚才说到的两个日志相关的监听器

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener,\
org.springframework.boot.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.logging.LoggingApplicationListener

ClasspathLoggingApplicationListener监听代码很简单,主要是监听spring应用启动或者启动失败类型事件,在收到对应事件时打印debug日志输出当前classLoader中urls列表

LoggingApplicationListener

LoggingApplicationListener监听器监听ApplicationStartingEvent、ApplicationEnvironmentPreparedEvent、ApplicationPreparedEvent、ContextClosedEvent、ApplicationFailedEvent五种类型的事件。并且Source类型为:SpringApplication、ApplicationContext。常用的source为SpringApplication类型。例如:

@SpringBootApplication
@EnableDubbo(multipleConfig = true)
public class Application {
	public static void main(String[] args) {
		new SpringApplication(Application.class).run(args);
	}
}

事件发送顺序

  1. ApplicationStartedEvent(ApplicationStartingEvent子类)
  2. ApplicationEnvironmentPreparedEvent
  3. ApplicationPreparedEvent

我们按照顺序查看该监听器的对应动作

onApplicationStartingEvent

private void onApplicationStartingEvent(ApplicationStartingEvent event) {
	this.loggingSystem = LoggingSystem
			.get(event.getSpringApplication().getClassLoader());
	this.loggingSystem.beforeInitialize();
}
  1. 根据source(例如:案例中的Application.class)的classloader获取日志系统实例LoggingSystem
  2. 回调日志系统初始化前的前置动作beforeInitialize

获取日志系统实例

// LoggingSystem.class
public static LoggingSystem get(ClassLoader classLoader) {
	String loggingSystem = System.getProperty(SYSTEM_PROPERTY);
	if (StringUtils.hasLength(loggingSystem)) {
		if (NONE.equals(loggingSystem)) {
			return new NoOpLoggingSystem();
		}
		return get(classLoader, loggingSystem);
	}
	for (Map.Entry<String, String> entry : SYSTEMS.entrySet()) {
		if (ClassUtils.isPresent(entry.getKey(), classLoader)) {
			return get(classLoader, entry.getValue());
		}
	}
	throw new IllegalStateException("No suitable logging system located");
}
  1. 如果配置了System的LoggingSystem属性指定日志系统类,则根据配置获取日志系统实例
  2. 如果没有配置System的LoggingSystem的系统属性,遍历_SYSTEMS配置,返回第一个匹配的日志系统实例_
// LoggingSystem
static {
	Map<String, String> systems = new LinkedHashMap<String, String>();
	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");
	SYSTEMS = Collections.unmodifiableMap(systems);
}

根据日志系统类获取实例

private static LoggingSystem get(ClassLoader classLoader, String loggingSystemClass) {
	try {
		Class<?> systemClass = ClassUtils.forName(loggingSystemClass, classLoader);
		return (LoggingSystem) systemClass.getConstructor(ClassLoader.class)
				.newInstance(classLoader);
	}
	catch (Exception ex) {
		throw new IllegalStateException(ex);
	}
}

springboot提供了三种实现

  1. LogbackLoggingSystem
  2. Log4J2LoggingSystem
  3. JavaLoggingSystem

除了JavaLoggingSystem,其他两种均是Slf4JLoggingSystem子类

初始化日志系统前置动作

目前系统中使用的log4j2日志,所以以Log4J2LoggingSystem为例

public void beforeInitialize() {
	LoggerContext loggerContext = getLoggerContext();
	if (isAlreadyInitialized(loggerContext)) {
		return;
	}
	super.beforeInitialize();
	loggerContext.getConfiguration().addFilter(FILTER);
}
  1. 获取日志上下文并初始化,如果已经初始化则直接返回
  2. 调用父类前置动作,如果存在org.slf4j.bridge.SLF4JBridgeHandler句柄类则移除jdk桥接句柄removeJdkLoggingBridgeHandler安装SLF4JBridgeHandler
  3. 添加Log4J2LoggingSystem.FILTER过滤器,拒绝所有日志

onApplicationEnvironmentPreparedEvent

// LoggingSystem.class
private void onApplicationEnvironmentPreparedEvent(
		ApplicationEnvironmentPreparedEvent event) {
	...
	initialize(event.getEnvironment(), event.getSpringApplication().getClassLoader());
}
// LoggingSystem.class
protected void initialize(ConfigurableEnvironment environment,
		ClassLoader classLoader) {
	new LoggingSystemProperties(environment).apply();
	LogFile logFile = LogFile.get(environment);
	if (logFile != null) {
		logFile.applyToSystemProperties();
	}
	initializeEarlyLoggingLevel(environment);
	initializeSystem(environment, this.loggingSystem, logFile);
	initializeFinalLoggingLevels(environment, this.loggingSystem);
	registerShutdownHookIfNecessary(environment, this.loggingSystem);
}
// Log4J2LoggingSystem.class
public void initialize(LoggingInitializationContext initializationContext,
		String configLocation, LogFile logFile) {
	LoggerContext loggerContext = getLoggerContext();
	...
	loggerContext.getConfiguration().removeFilter(FILTER);
	super.initialize(initializationContext, configLocation, logFile);
	markAsInitialized(loggerContext);
}
  1. 将应用Environment环境中的logging.前缀配置填充至System
  2. 如果存在logging.file、logging.path配置则创建对应的LogFile
  3. 如果应用Environment环境中配置debug或trace为true,则设置springBootLogging为对应的日志级别
  4. 如果应用Environment环境中logging.config配置了文件并且没有-D开头的参数配置,则使用配置文件初始化,否则使用null作为logConfig初始化。回调日志系统初始化
  5. 移除Log4J2LoggingSystem.FILTER过滤器(拒绝所有日志)
  6. 如果指定了configLocation,使用configLocation配置初始化initializeWithSpecificConfig
  7. 否则initializeWithConventions
  8. 标记当前初始化完毕

initializeWithSpecificConfig

  1. 回调子类loadConfiguration加载配置
  2. 创建ConfigurationSource配置文件源
  3. ConfigurationFactory工厂类创建Configuration配置实例
  4. 按照Configuration配置实例启动日志上下文LoggerContext.start(Configuration)
protected void loadConfiguration(LoggingInitializationContext initializationContext,
		String location, LogFile logFile) {
	super.loadConfiguration(initializationContext, location, logFile);
	loadConfiguration(location, logFile);
}
protected void loadConfiguration(String location, LogFile logFile) {
	Assert.notNull(location, "Location must not be null");
	try {
		LoggerContext ctx = getLoggerContext();
		URL url = ResourceUtils.getURL(location);
		ConfigurationSource source = getConfigurationSource(url);
		ctx.start(ConfigurationFactory.getInstance().getConfiguration(ctx, source));
	}
	catch (Exception ex) {
		throw new IllegalStateException(
				"Could not initialize Log4J2 logging from " + location, ex);
	}
}

initializeWithConventions

  1. 回调子类获取标准配置文件名列表getStandardConfigLocations,resource中查找标准配置文件,如果当前系统支持yml则同样查找log4j2.yaml配置,如果当前系统支持json则同样查找log4j2.json配置,否则使用log4j2.xml。如果resource中存在对应配置,则使用自身配置重新初始化
  2. 获取spring初始化配置文件,使用子类标准配置文件名列表,名称增加-spring后缀,例如:log4j2-spring.xml
  3. 如果配置文件不为空则按照配置文件初始化日志上下文,逻辑同initializeWithSpecificConfig
  4. 如果配置文件为空则加载默认配置并初始化,默认配置文件在当前目录下,如果logFile不为空则使用log4j2-file.xml配置,否则使用log4j2.xml配置

在这里插入图片描述

private void initializeWithConventions(
		LoggingInitializationContext initializationContext, LogFile logFile) {
	String config = getSelfInitializationConfig();
	if (config != null && logFile == null) {
		// self initialization has occurred, reinitialize in case of property changes
		reinitialize(initializationContext);
		return;
	}
	if (config == null) {
		config = getSpringInitializationConfig();
	}
	if (config != null) {
		loadConfiguration(initializationContext, config, logFile);
		return;
	}
	loadDefaults(initializationContext, logFile);
}

initializeFinalLoggingLevels

初始化设置最终的日志级别

registerShutdownHookIfNecessary

注册钩子如果需要的话

onApplicationPreparedEvent

将当前日志系统注册至spring容器

private void onApplicationPreparedEvent(ApplicationPreparedEvent event) {
	ConfigurableListableBeanFactory beanFactory = event.getApplicationContext()
			.getBeanFactory();
	if (!beanFactory.containsBean(LOGGING_SYSTEM_BEAN_NAME)) {
		beanFactory.registerSingleton(LOGGING_SYSTEM_BEAN_NAME, this.loggingSystem);
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,我可以为您提供一些关于Spring Boot整合Log4j2的指导。 首先,您需要在Spring Boot项目的pom.xml文件中添加Log4j2相关依赖。以下是一个示例: ```xml <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId> </dependency> </dependencies> ``` 接下来,您需要在项目中创建log4j2.xml文件。以下是一个示例: ```xml <?xml version="1.0" encoding="UTF-8"?> <Configuration status="WARN"> <Appenders> <Console name="console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> </Console> </Appenders> <Loggers> <Root level="info"> <AppenderRef ref="console"/> </Root> </Loggers> </Configuration> ``` 在这个示例中,我们定义了一个名为“console”的Appender,用于将日志输出到控制台。我们还定义了一个名为“Root”的Logger,用于将所有日志消息发送到“console”Appender,并将日志级别设置为“info”。 最后,在项目中使用Log4j2日志记录。您可以使用Spring的内置日志记录注解@Log4j2来注释您的类。以下是一个示例: ```java import lombok.extern.log4j.Log4j2; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; @Controller @Log4j2 public class HomeController { @GetMapping("/") public String home() { log.info("Home page accessed"); return "home"; } } ``` 这个示例中,我们使用了Lombok提供的@Log4j2注解来注释HomeController类。然后,我们在home()方法中使用log.info()方法来记录日志消息。 希望这些指导对您有所帮助!
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值