// 用法
@SpringBootApplication
public class App {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(App.class, args);
// 获取生成Logger的工厂,通过该工厂生成Logger对象
// 该ILoggerFactory对象实际上就是Logback的LoggerContext上下文对象
// 这个系统都只有唯一的LoggerContext上下文,即只有一个LoggerFactory
// 因此,LoggerFactory.getLogger虽然调用的是静态方法,但最终都是通过系统的单例ILoggerFactory(LoggerContext)对象来创建Logger对象
// 即getLogger最终都会调用ch.qos.logback.classic.LoggerContext.getLogger(java.lang.String)
// 因此,只需要根据指定的日志框架配置文件初始化唯一的一份LoggerContext,从而整个项目获取的Logger对象,都是统一的配置的,也就是同一个LoggerContext生成的
// 并且生成的Logger对象,相同名称的Logger会被缓存起来
ILoggerFactory logFactory = LoggerFactory.getILoggerFactory();
// 如果Logger的名称为ROOT,是一个特殊的Logger,全局只有一份ROOT的Logger对象,并且默认的级别为Debug
Logger luck = logFactory.getLogger("ROOT");
Logger logger = LoggerFactory.getLogger(App.class);
Logger logger1 = LoggerFactory.getLogger(App.class);
logger.error("hello error");
}
}
// SpringBoot的日志是由这个监听器来处理的它是一个ApplicationListener
// 为什么日志相关的配置都需要保存到系统变量中?
// 因为只有放在系统变量中,其他的Java日志框架才能获取到对应的变量值,从而按照一定规则,配置或者格式打印日志
public class LoggingApplicationListener implements GenericApplicationListener {
// 日志级别
private static final ConfigurationPropertyName LOGGING_LEVEL = ConfigurationPropertyName.of("logging.level");
// 日志分组
private static final ConfigurationPropertyName LOGGING_GROUP = ConfigurationPropertyName.of("logging.group");
// 日志级别的map, 日志名称 -> 日志级别的映射,例如: luck.spring: debug
private static final Bindable<Map<String, LogLevel>> STRING_LOGLEVEL_MAP = Bindable.mapOf(String.class, LogLevel.class);
// 日志分组的map,分组名称->所有日志名称的集合映射, web -> ["org.a","org.b"]
private static final Bindable<Map<String, List<String>>> STRING_STRINGS_MAP = Bindable.of(ResolvableType.forClassWithGenerics(MultiValueMap.class, String.class, String.class).asMap());
// LoggingApplicationListener的默认顺序
public static final int DEFAULT_ORDER = Ordered.HIGHEST_PRECEDENCE + 20;
// logback.xml的配置文件路径
public static final String CONFIG_PROPERTY = "logging.config";
// 是否允许注册钩子函数的变量配置
public static final String REGISTER_SHUTDOWN_HOOK_PROPERTY = "logging.register-shutdown-hook";
// SpringBoog日志系统的Bean名称
public static final String LOGGING_SYSTEM_BEAN_NAME = "springBootLoggingSystem";
// SpringBoog日志文件配置的Bean名称
public static final String LOG_FILE_BEAN_NAME = "springBootLogFile";
// SpringBoog日志分组的Bean名称
public static final String LOGGER_GROUPS_BEAN_NAME = "springBootLoggerGroups";
// 当前日志监听器支持的事件类型
private static final Class<?>[] EVENT_TYPES = {ApplicationStartingEvent.class, ApplicationEnvironmentPreparedEvent.class, ApplicationPreparedEvent.class, ContextClosedEvent.class, ApplicationFailedEvent.class};
// 是否允许注册关闭的钩子
private static final AtomicBoolean shutdownHookRegistered = new AtomicBoolean();
// 打印日志的对象,通过门面获取
private final Log logger = LogFactory.getLog(getClass());
// 使用具体的打印系统,也就是具体的日志实现,LoggingSystem为抽象类,默认实现为LogbackLoggingSystem
// private LoggingSystem loggingSystem;
private LogbackLoggingSystem loggingSystem;
// 日志文件配置信息
private LogFile logFile;
// 日志分组信息
private LoggerGroups loggerGroups;
// SpringBoot的日志级别
private LogLevel springBootLogging = null;
// 默认的日志分组,以及对应的日志名称
private static final Map<String, List<String>> DEFAULT_GROUP_LOGGERS;
// SpringBoot的日志分组,这是在SpringBoot内置打印日志的配置分组和日志级别信息
private static final Map<LogLevel, List<String>> SPRING_BOOT_LOGGING_LOGGERS;
static {
// 默认的日志分组,以及对应的日志名称
MultiValueMap<String, String> loggers = new LinkedMultiValueMap<>();
loggers.add("web", "org.springframework.core.codec");
loggers.add("web", "org.springframework.http");
loggers.add("web", "org.springframework.web");
loggers.add("web", "org.springframework.boot.actuate.endpoint.web");
loggers.add("web", "org.springframework.boot.web.servlet.ServletContextInitializerBeans");
loggers.add("sql", "org.springframework.jdbc.core");
loggers.add("sql", "org.hibernate.SQL");
loggers.add("sql", "org.jooq.tools.LoggerListener");
DEFAULT_GROUP_LOGGERS = Collections.unmodifiableMap(loggers);
// SpringBoot的日志分组,这是在SpringBoot内置打印日志的配置分组和日志级别信息
MultiValueMap<LogLevel, String> loggers = new LinkedMultiValueMap<>();
loggers.add(LogLevel.DEBUG, "sql");
loggers.add(LogLevel.DEBUG, "web");
loggers.add(LogLevel.DEBUG, "org.springframework.boot");
loggers.add(LogLevel.TRACE, "org.springframework");
loggers.add(LogLevel.TRACE, "org.apache.tomcat");
loggers.add(LogLevel.TRACE, "org.apache.catalina");
loggers.add(LogLevel.TRACE, "org.eclipse.jetty");
loggers.add(LogLevel.TRACE, "org.hibernate.tool.hbm2ddl");
SPRING_BOOT_LOGGING_LOGGERS = Collections.unmodifiableMap(loggers);
}
@Override
public void onApplicationEvent(ApplicationEvent event) {
// SpringApplication启动事件
if (event instanceof ApplicationStartingEvent) {
this.onApplicationStartingEvent((ApplicationStartingEvent) event);
}
// 环境预处理完成事件
if (event instanceof ApplicationEnvironmentPreparedEvent) {
this.onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
}
// 上下文已经处理好了的回调
if (event instanceof ApplicationPreparedEvent) {
this.onApplicationPreparedEvent((ApplicationPreparedEvent) event);
}
// 容器关闭事件
if (event instanceof ContextClosedEvent && ((ContextClosedEvent) event).getApplicationContext().getParent() == null) {
this.onContextClosedEvent();
}
// 容器启动失败
if (event instanceof ApplicationFailedEvent) {
this.onApplicationFailedEvent();
}
}
// SpringApplication启动事件
private void onApplicationStartingEvent(ApplicationStartingEvent event) {
// 初始化日志系统,就是具体打印日志的实现
this.loggingSystem = LogbackLoggingSystem.get(event.getSpringApplication().getClassLoader());
// 初始化日志系统
this.loggingSystem.beforeInitialize();
}
// 环境预处理完成事件
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
SpringApplication springApplication = event.getSpringApplication();
// 如果日志系统没有实例化
if (this.loggingSystem == null) {
// 获取一个日志系统
this.loggingSystem = LogbackLoggingSystem.get(springApplication.getClassLoader());
}
// 初始化日志配置信息
this.initialize(event.getEnvironment(), springApplication.getClassLoader());
}
// 上下文已经处理好了的回调
private void onApplicationPreparedEvent(ApplicationPreparedEvent event) {
ConfigurableListableBeanFactory beanFactory = event.getApplicationContext().getBeanFactory();
// 将日志系统设置为Bean
if (!beanFactory.containsBean(LOGGING_SYSTEM_BEAN_NAME)) {
beanFactory.registerSingleton(LOGGING_SYSTEM_BEAN_NAME, this.loggingSystem);
}
// 将日志文件配置信息注册为Bean
if (this.logFile != null && !beanFactory.containsBean(LOG_FILE_BEAN_NAME)) {
beanFactory.registerSingleton(LOG_FILE_BEAN_NAME, this.logFile);
}
// 将日志分组信息注册为Bean
if (this.loggerGroups != null && !beanFactory.containsBean(LOGGER_GROUPS_BEAN_NAME)) {
beanFactory.registerSingleton(LOGGER_GROUPS_BEAN_NAME, this.loggerGroups);
}
}
// 清理日志系统信息
private void onContextClosedEvent() {
if (this.loggingSystem != null) {
this.loggingSystem.cleanUp();
}
}
// 清理日志系统信息
private void onApplicationFailedEvent() {
if (this.loggingSystem != null) {
this.loggingSystem.cleanUp();
}
}
// 初始化日志配置信息
protected void initialize(ConfigurableEnvironment environment, ClassLoader classLoader) {
// 获取日志系统的配置属性,最终都是返回一个new LoggingSystemProperties(environment)对象,从环境对象中获取配置属性
LoggingSystemProperties systemProperties = this.getLoggingSystemProperties(environment);
// 初始化日志配置信息,保存到系统变量中
systemProperties.apply();
// 初始化日志文件配置
this.logFile = LogFile.get(environment);
// 如果存在日志文件相关配置
if (this.logFile != null) {
// 将当前对象中路径和文件名保存到系统变量中
this.logFile.applyToSystemProperties();
}
// 日志分组,DEFAULT_GROUP_LOGGERS在静态代码块了设置了很多分组
this.loggerGroups = new LoggerGroups(DEFAULT_GROUP_LOGGERS);
// 初始化SpirngBoot的日志级别,取决于配置变量的debug和trace配置,这里是提前设置,最后会被覆盖
this.initializeEarlyLoggingLevel(environment);
// 初始化日志系统
this.initializeSystem(environment, this.loggingSystem, this.logFile);
// 初始化最终的日志级别
this.initializeFinalLoggingLevels(environment, this.loggingSystem);
// 是否有必要注册解析的钩子函数
this.registerShutdownHookIfNecessary(environment, this.loggingSystem);
}
// 获取日志系统的配置属性,最终都是返回一个new LoggingSystemProperties(environment)对象,从环境对象中获取配置属性
private LoggingSystemProperties getLoggingSystemProperties(ConfigurableEnvironment environment) {
return (this.loggingSystem != null) ? this.loggingSystem.getSystemProperties(environment) : new LoggingSystemProperties(environment);
}
// 初始化SpirngBoot的日志级别
private void initializeEarlyLoggingLevel(ConfigurableEnvironment environment) {
// 如果需要解析参数,并且SpringBoot的日志级别还未初始化
if (this.parseArgs && this.springBootLogging == null) {
// 如果设置了debug这个key,则springboot的日志级别设为DEBUG
if (this.isSet(environment, "debug")) {
this.springBootLogging = LogLevel.DEBUG;
}
// 如果设置了trace这个key,则springboot的日志级别设为TRACE
if (this.isSet(environment, "trace")) {
this.springBootLogging = LogLevel.TRACE;
}
}
}
// 配置属性是否设置了debug,或者trace的key
private boolean isSet(ConfigurableEnvironment environment, String property) {
// 从环境对象中获取指定属性的属性值
String value = environment.getProperty(property);
// 返回对应的值是否为true
return (value != null && !value.equals("false"));
}
// 初始化日志系统(日志实现)
private void initializeSystem(ConfigurableEnvironment environment, LogbackLoggingSystem system, LogFile logFile) {
// 获取logging.config配置的值
String logConfig = StringUtils.trimWhitespace(environment.getProperty(CONFIG_PROPERTY));
LoggingInitializationContext initializationContext = new LoggingInitializationContext(environment);
// 是否忽略该logging.config配置的logback配置文件路径
if (this.ignoreLogConfig(logConfig)) {
// 如果忽略,那么,就使用默认路径logback配置文件
system.initialize(initializationContext, null, logFile);
} else {
// 指定了logback配置文件路径,加载指定路径的配置文件
system.initialize(initializationContext, logConfig, logFile);
}
}
// 是否忽略该logging.config配置的logback配置文件路径
private boolean ignoreLogConfig(String logConfig) {
// 如果没有配置logging.config,或者值为-D开头,则忽略该配置文件
return !StringUtils.hasLength(logConfig) || logConfig.startsWith("-D");
}
// 初始化最终的日志级别
private void initializeFinalLoggingLevels(ConfigurableEnvironment environment, LogbackLoggingSystem system) {
// 绑定日志分组
this.bindLoggerGroups(environment);
// 如果之前设置了SpringBoot的日志级别
if (this.springBootLogging != null) {
// 重新初始化SpringBoot的日志级别
// 这里设置的是SpringBoot自身对不同包的日志级别配置
this.initializeSpringBootLogging(system, this.springBootLogging);
}
// 绑定logging.level的值,设置日志的级别,包括分组中的所有日志名称
// 这里是用户自己在配置文件配置的日志名称->日志级别
this.setLogLevels(system, environment);
}
// 绑定日志分组
private void bindLoggerGroups(ConfigurableEnvironment environment) {
if (this.loggerGroups != null) {
// 将logging.group的配置绑定到MultiValueMap对象中,设置到loggerGroups变量中
Binder binder = Binder.get(environment);
binder.bind(LOGGING_GROUP, STRING_STRINGS_MAP).ifBound(this.loggerGroups::putAll);
}
}
// 初始化SpringBoot的日志级别
// 这里设置的是SpringBoot自身对不同包的日志级别配置,和我们自身没多大关系,我们自身的配置在logging.level中
protected void initializeSpringBootLogging(LogbackLoggingSystem system, LogLevel springBootLogging) {
// 获取日志级别的配置器
BiConsumer<String, LogLevel> configurer = this.getLogLevelConfigurer(system);
// 获取SpringBoot日志级别对应的所有日志名称,设置这些日志名称的日志分组和日志级别
// 这里设置的是SpringBoot自身对不同包的日志级别配置,和我们自身没多大关系,我们自身的配置在logging.level中
SPRING_BOOT_LOGGING_LOGGERS.getOrDefault(springBootLogging, Collections.emptyList()).forEach((name) -> this.configureLogLevel(name, springBootLogging, configurer));
}
// 绑定logging.level的值,设置日志的级别,包括分组中的所有日志名称
// 这里是用户自己在配置文件配置的日志名称->日志级别
protected void setLogLevels(LogbackLoggingSystem system, ConfigurableEnvironment environment) {
// 获取日志级别的配置器
BiConsumer<String, LogLevel> customizer = this.getLogLevelConfigurer(system);
Binder binder = Binder.get(environment);
// 将环境配置中的logging.level变量值对应的级别绑定到Map<String, LogLevel>中,name->level
// 这里是用户自己在配置文件配置的日志名称->日志级别
Map<String, LogLevel> levels = binder.bind(LOGGING_LEVEL, STRING_LOGLEVEL_MAP).orElseGet(Collections::emptyMap);
// 遍历所有配置的name->日志级别的映射,一一设置这些日志名称的日志分组和日志级别
levels.forEach((name, level) -> this.configureLogLevel(name, level, customizer));
}
// 设置这些日志名称的日志分组和日志级别
private void configureLogLevel(String name, LogLevel level, BiConsumer<String, LogLevel> configurer) {
// 如果存在日志分组
if (this.loggerGroups != null) {
// 获取到当前日志名称的分组
LoggerGroup group = this.loggerGroups.get(name);
// 如果该日志名称存在分组信息,并且其他的日志名称
if (group != null && group.hasMembers()) {
// 配置该分组的日志级别,将该分组的所有日志名称都设置为统一日志级别
group.configureLogLevel(level, configurer);
return;
}
}
// 配置单个日志名称的日志级别
configurer.accept(name, level);
}
// 获取日志级别的配置器
private BiConsumer<String, LogLevel> getLogLevelConfigurer(LogbackLoggingSystem system) {
return new BiConsumer<String, LogLevel>() {
@Override
public void accept(String name, LogLevel logLevel) {
try {
// 获取日志系统的日志名称,默认为ROOT
name = name.equalsIgnoreCase(LogbackLoggingSystem.ROOT_LOGGER_NAME) ? null : name;
// 设置对应名称的日志级别
system.setLogLevel(name, level);
} catch (RuntimeException ex) {
this.logger.error(LogMessage.format("Cannot set level '%s' for '%s'", level, name));
}
}
};
}
// 是否有必要注册解析的钩子函数
private void registerShutdownHookIfNecessary(Environment environment, LogbackLoggingSystem loggingSystem) {
// 如果系统变量值中存在logging.register-shutdown-hook值,默认为true
if (environment.getProperty(REGISTER_SHUTDOWN_HOOK_PROPERTY, Boolean.class, true)) {
// 获取对应的构造函数
Runnable shutdownHandler = loggingSystem.getShutdownHandler();
// 如果允许注册钩子函数
if (shutdownHandler != null && shutdownHookRegistered.compareAndSet(false, true)) {
// 注册钩子函数
SpringApplication.getShutdownHandlers().add(shutdownHandler);
}
}
}
/**
* 设置用于Spring Boot和相关库的自定义日志级别
*/
public void setSpringBootLogging(LogLevel springBootLogging) {
this.springBootLogging = springBootLogging;
}
}
// 不同日志实现对应不同的配置属性,这是一个抽象类,我们讲logback
public class LoggingSystemProperties {
// 日志文件配置的路径保存在系统变量中的KEY
public static final String LOG_PATH = "LOG_PATH";
public static final String LOG_FILE = "LOG_FILE";
// 将key和value设置到系统变量中
protected final void setSystemProperty(String name, String value) {
// 解析Spel表达式
String value = resolver.getProperty(propertyName);
value = (value != null) ? value : defaultValue;
// 如果系统变量中不存在,则添加到系统变量中
if (System.getProperty(name) == null && value != null) {
System.setProperty(name, value);
}
}
// 执行属性配置
protected void apply(LogFile logFile, PropertyResolver resolver) {
// LoggingSystemProperties:公共逻辑
super.apply(logFile, resolver) {
// 如果系统变量中不存在该配置,从环境对象中获取logging.exception-conversion-word,并设置都系统变量中
this.setSystemProperty(resolver, EXCEPTION_CONVERSION_WORD, "logging.exception-conversion-word")
// 下面的属性都是一样,从环境对象中获取,也就是从配置文件或者命令行中获取
this.setSystemProperty(PID_KEY, new ApplicationPid().toString());
this.setSystemProperty(resolver, CONSOLE_LOG_PATTERN, "logging.pattern.console");
this.setSystemProperty(resolver, CONSOLE_LOG_CHARSET, "logging.charset.console", getDefaultCharset().name());
this.setSystemProperty(resolver, LOG_DATEFORMAT_PATTERN, "logging.pattern.dateformat");
this.setSystemProperty(resolver, FILE_LOG_PATTERN, "logging.pattern.file");
this.setSystemProperty(resolver, FILE_LOG_CHARSET, "logging.charset.file", getDefaultCharset().name());
this.setSystemProperty(resolver, LOG_LEVEL_PATTERN, "logging.pattern.level");
// 如果存在日志文件,应用日志文件的属性配置
if (logFile != null) {
logFile.applyToSystemProperties();
}
}
// logback逻辑
// 配置logback专有的属性
// 如果系统变量中不存在该配置,从环境对象中获取logging.logback.rollingpolicy.file-name-pattern,并设置都系统变量中
// 第一个参数: 占位符字符串解析器
// 第二个参数: 系统环境变量中存入的Key
// 第三个参数: 最新配置文件key的名称
// 第四个参数: 兼容之前配置文件key的名称,这些名称已经过期不推荐使用
applyRollingPolicy(resolver, ROLLINGPOLICY_FILE_NAME_PATTERN, "logging.logback.rollingpolicy.file-name-pattern", "logging.pattern.rolling-file-name")
{
// 通过最新的配置名称从环境对象中的值,如果存在,解析系统变变量中spel表达式返回
T value = resolver.getProperty(propertyName, type);
// 如果环境对象中不存在该Key的值
if (value == null) {
// 使用以前过期的配置名称获取值并解析spel
value = resolver.getProperty(deprecatedPropertyName, type);
}
// 如果获取到配置的值
if (value != null) {
String stringValue = String.valueOf((value instanceof DataSize) ? ((DataSize) value).toBytes() : value);
// 将key和value设置到系统变量中
this.setSystemProperty(systemPropertyName, stringValue);
}
}
applyRollingPolicy(resolver, ROLLINGPOLICY_CLEAN_HISTORY_ON_START, "logging.logback.rollingpolicy.clean-history-on-start", "logging.file.clean-history-on-start");
applyRollingPolicy(resolver, ROLLINGPOLICY_MAX_FILE_SIZE, "logging.logback.rollingpolicy.max-file-size", "logging.file.max-size", DataSize.class);
applyRollingPolicy(resolver, ROLLINGPOLICY_TOTAL_SIZE_CAP, "logging.logback.rollingpolicy.total-size-cap", "logging.file.total-size-cap", DataSize.class);
applyRollingPolicy(resolver, ROLLINGPOLICY_MAX_HISTORY, "logging.logback.rollingpolicy.max-history", "logging.file.max-history");
}
}
// 日志文件配置对象,内部的变量与系统变量LOG_FILE,LOG_PATH同步
public class LogFile {
// 文件名配置的变量Key
public static final String FILE_NAME_PROPERTY = "logging.file.name";
// 文件路径配置的变量Key
public static final String FILE_PATH_PROPERTY = "logging.file.path";
// 文件名
private final String file;
// 文件路径
private final String path;
// 获取日志文件配置
public static LogFile get(PropertyResolver propertyResolver) {
// 获取环境对象中logging.file.name的值
String file = propertyResolver.getProperty(FILE_NAME_PROPERTY);
// 获取环境对象中logging.file.path的值
String path = propertyResolver.getProperty(FILE_PATH_PROPERTY);
// 存在路径或者文件名,则创建日志文件配置
if (StringUtils.hasLength(file) || StringUtils.hasLength(path)) {
return new LogFile(file, path);
}
// 如果都没配置,返回null
return null;
}
// 将当前对象中路径和文件名保存到系统变量中
public void applyToSystemProperties() {
// 将当前对象中路径和文件名保存到系统变量中
this.applyTo(System.getProperties());
}
// 将当前对象中路径和文件名保存到系统变量中
public void applyTo(Properties properties) {
// 将属性key和value(value不为空)保存到properties中,Key为: LOG_PATH
this.put(properties, LoggingSystemProperties.LOG_PATH, this.path);
// 将属性key和value(value不为空)保存到properties中,Key为: LOG_FILE
this.put(properties, LoggingSystemProperties.LOG_FILE, toString());
}
// 将当前对象中路径和文件名保存到系统变量中
private void put(Properties properties, String key, String value) {
// 如果存在值才会保存到系统变量中
if (StringUtils.hasLength(value)) {
properties.put(key, value);
}
}
}
// logback对日志系统的实现
public class LogbackLoggingSystem extends Slf4JLoggingSystem {
// 根日志使用的名称
public static final String ROOT_LOGGER_NAME = "ROOT";
// 过期的属性配置,该配置指定logback的配置文件路径,现在改为最新的"logging.config"配置
private static final String CONFIGURATION_FILE_PROPERTY = "logback.configurationFile";
// 从spring.factories文件中获取LoggingSystemFactory类型的日志工厂
private static final LoggingSystemFactory SYSTEM_FACTORY = LoggingSystemFactory.fromSpringFactories();
// 设置日志实现的日志级别
public void setLogLevel(String loggerName, LogLevel level) {
ch.qos.logback.classic.Logger logger = getLogger(loggerName);
if (logger != null) {
logger.setLevel(LEVELS.convertSystemToNative(level));
}
}
// 获取日志系统
public static LoggingSystem get(ClassLoader classLoader) {
// 获取LoggingSystem.class.getName()的系统属性名,配置日志系统实现的全类名
String loggingSystemClassName = System.getProperty(SYSTEM_PROPERTY);
// 如果存在LoggingSystem.class.getName()的系统属性配置
if (StringUtils.hasLength(loggingSystemClassName)) {
// 但是配置的为none,直接返回一个没有任何操作的日志系统
if (NONE.equals(loggingSystemClassName)) {
return new NoOpLoggingSystem();
}
// 使名称返回创建日志系统对象
Class<?> systemClass = ClassUtils.forName(loggingSystemClassName, classLoader);
Constructor<?> constructor = systemClass.getDeclaredConstructor(ClassLoader.class);
constructor.setAccessible(true);
return (LoggingSystem) constructor.newInstance(classLoader);
}
// 如果没有配置LoggingSystem.class.getName()的系统属性值
// 则从spring.factories文件中获取LoggingSystemFactory类型的日志工厂,直到可以生成日志系统,否则抛出异常
LoggingSystem loggingSystem = SYSTEM_FACTORY.getLoggingSystem(classLoader);
Assert.state(loggingSystem != null, "No suitable logging system located");
return loggingSystem;
}
// 创建Logger对象
private ch.qos.logback.classic.Logger getLogger(String name) {
LoggerContext factory = getLoggerContext();
return factory.getLogger(getLoggerName(name));
}
// 获取日志名称
private String getLoggerName(String name) {
// 如果日志名称为空或者为ROOT,返回根日志名称
if (!StringUtils.hasLength(name) || Logger.ROOT_LOGGER_NAME.equals(name)) {
return ROOT_LOGGER_NAME;
}
// 否则返回指定的日志名称
return name;
}
// 日志系统关闭的钩子函数
public Runnable getShutdownHandler() {
return () -> getLoggerContext().stop();
}
// logback的配置属性
@Override
public LoggingSystemProperties getSystemProperties(ConfigurableEnvironment environment) {
return new LoggingSystemProperties(environment);
}
// logback支持的配置文件
@Override
protected String[] getStandardConfigLocations() {
return new String[]{"logback-test.groovy", "logback-test.xml", "logback.groovy", "logback.xml"};
}
//
/**
* 初始化日志系统
*
* @param initializationContext 日志初始化上下文,默认为new LoggingInitializationContext(environment);
* @param configLocation logback配置文件路径,默认为"logging.config"配置的路径,如果没有配置则为null
* @param logFile 日志文件的配置
*/
@Override
public void initialize(LoggingInitializationContext initializationContext, String configLocation, LogFile logFile) {
// 获取日志上下文(创建日志的工厂)
LoggerContext loggerContext = this.getLoggerContext();
// 是否已经初始化了,已经初始化了就不处理
if (this.isAlreadyInitialized(loggerContext)) {
return;
}
// 初始化系统
if (StringUtils.hasLength(configLocation)) {
// 初始化特殊的配置,也就是指定了配置文件的路径名称(存在该"logging.config"配置值)
// 没有指定那就是默认的logback.xml
this.initializeWithSpecificConfig(initializationContext, configLocation, logFile);
return;
}
// 如果没有指定配置文件路径,则初始化约定(也就是默认的)的配置文件信息
this.initializeWithConventions(initializationContext, logFile);
// 标记日志系统已初始化
this.markAsInitialized(loggerContext);
if (StringUtils.hasText(System.getProperty(CONFIGURATION_FILE_PROPERTY))) {
this.getLogger(LogbackLoggingSystem.class.getName()).warn("Ignoring '" + CONFIGURATION_FILE_PROPERTY + "' system property. Please use 'logging.config' instead.");
}
}
// 初始化特殊的配置,也就是指定了配置文件的路径名称(存在该"logging.config"配置值)
// 没有指定那就是默认的logback.xml
private void initializeWithSpecificConfig(LoggingInitializationContext initializationContext, String configLocation, LogFile logFile) {
// 解析路径中的占位符
configLocation = SystemPropertyUtils.resolvePlaceholders(configLocation);
// 加载日志配置文件,并给日志系统进行设置
this.loadConfiguration(initializationContext, configLocation, logFile);
}
// 加载日志配置文件,并给日志系统进行设置
protected void loadConfiguration(LoggingInitializationContext initializationContext, String location, LogFile logFile) {
super.loadConfiguration(initializationContext, location, logFile) {
if (initializationContext != null) {
// 根据给定的日志文件配置信息重新加载一份配置属性,因为LogFile可以被修改,同时日志文件配置信息需要保存到系统变量中,而且需要同步更新
this.applySystemProperties(initializationContext.getEnvironment(), logFile) {
new LoggingSystemProperties(environment).apply(logFile);
}
}
}
// 获取日志上下文
LoggerContext loggerContext = this.getLoggerContext();
// 停止日志系统并重置配置信息
this.stopAndReset(loggerContext);
// 根据logback配置文件路径的,默认为"logging.config"配置的路径,如果没有配置则为null,获取到文件资源
URL url = ResourceUtils.getURL(location);
// 加载logback.xml配置文件内容,对日志系统进行配置
this.configureByResourceUrl(initializationContext, loggerContext, url);
}
// 根据当前日志实现(Logback)获取到该实现的配置文件
protected String getSelfInitializationConfig() {
// logback支持的配置文件
// return new String[]{"logback-test.groovy", "logback-test.xml", "logback.groovy", "logback.xml"};
String[] standardConfigLocations = this.getStandardConfigLocations();
// 通过支持的配置找符合条件的配置文件
return this.findConfig(standardConfigLocations);
}
// 通过该日志实现(Logback)支持的配置找符合条件的配置文件
private String findConfig(String[] locations) {
// 遍历所有的文件路径
for (String location : locations) {
// 默认为classpath下的路径
ClassPathResource resource = new ClassPathResource(location, this.classLoader);
if (resource.exists()) {
// 如果classpath下支持的文件名存在,返回该资源信息
return "classpath:" + location;
}
}
return null;
}
// 初始化约定(也就是默认的)的配置文件信息
private void initializeWithConventions(LoggingInitializationContext initializationContext, LogFile logFile) {
// 根据当前日志实现(Logback)获取到该实现的配置文件
String config = this.getSelfInitializationConfig();
// 因为initialize方法是public,这使得我们可以动态的修改日志配置信息
// 当classpath下,存在默认的配置文件的时候{"logback-test.groovy", "logback-test.xml", "logback.groovy", "logback.xml"}
// 我们就要根据传入进来的日志文件配置信息做判断,因为LogFile中存入的系统变量的参数可以在logback.xml中应用,例如:"${LOG_PATH}" "${LOG_FILE}";
// logFile最终的作用是将logFile配置的路径保存到系统变量中通过这两个"${LOG_PATH}" "${LOG_FILE}"Key保存
// 如果没有给定,那么就不需要对logFile的变量进行操作,直接重新初始化一次,加载默认的配置即可,不用对"${LOG_PATH}" "${LOG_FILE}"进行任何更改
// 如果给定了,那么就要将logFile变量中的值保存到系统变量中
if (config != null && logFile == null) {
// 对日志系统进行重新初始化,重新加载日志系统配置文件,不修改日志文件的系统变量
this.reinitialize(initializationContext);
return;
}
// 如果不存在约定的的日志配置文件 {"logback-test.groovy", "logback-test.xml", "logback.groovy", "logback.xml"}
if (config == null) {
// 再找找与spring相关的logback配置文件
// 找类路径下的xxx-spring.xxx (logback-spring.xml)配置
config = this.getSpringInitializationConfig();
}
// 如果找到了logback默认的或者拼接上-spring的配置文件
if (config != null) {
// 加载该配置文件信息,并修改日志文件的系统变量
this.loadConfiguration(initializationContext, config, logFile);
return;
}
// 如果不存在logback.xml配置文件,那么就需要加载lombok的默认配置
this.loadDefaults(initializationContext, logFile);
}
// 加载默认配置
protected void loadDefaults(LoggingInitializationContext initializationContext, LogFile logFile) {
// 获取日志上下文
LoggerContext context = this.getLoggerContext();
// 停止日志系统并重置配置信息
this.stopAndReset(context);
// 获取系统变量中,logback.debug的值
boolean debug = Boolean.getBoolean("logback.debug");
// 如果开启了debug
if (debug) {
// 添加在控制台打印的状态监听器
StatusListenerConfigHelper.addOnConsoleListenerInstance(context, new OnConsoleStatusListener());
}
// 获取环境对象
Environment environment = initializationContext.getEnvironment();
// 在同一个JVM运行多个应用程序的情况下,直接应用系统属性,将系统属性保存到LoggingSystemProperties属性配置中
new LoggingSystemProperties(environment, context::putProperty).apply(logFile);
// 创建logback的配置器,可以通过api进行配置,比xml解析要快
LogbackConfigurator configurator = debug ? new DebugLogbackConfigurator(context) : new LogbackConfigurator(context);
// 应用日志文件信息,配置默认的logback配置,例如控制台格式等等
new DefaultLogbackConfiguration(logFile).apply(configurator);
context.setPackagingDataEnabled(true);
}
// 找类路径下的xxx-spring.xxx (logback-spring.xml)配置
protected String getSpringInitializationConfig() {
// 获取spring支持的配置文件名称
// xxx-spring.xxx (logback-spring.xml)
String[] springConfigLocations = this.getSpringConfigLocations();
// 找类路径下的对应的配置文件
return this.findConfig(springConfigLocations);
}
// 获取spring支持的配置文件名称
// xxx-spring.xxx (logback-spring.xml)
protected String[] getSpringConfigLocations() {
// 获取logback约定支持的配置文件
String[] locations = this.getStandardConfigLocations();
for (int i = 0; i < locations.length; i++) {
// 获取扩展名
String extension = StringUtils.getFilenameExtension(locations[i]);
// 将路径拼接为 xxx-spring.xxx (logback-spring.xml)
locations[i] = locations[i].substring(0, locations[i].length() - extension.length() - 1) + "-spring." + extension;
}
return locations;
}
// 对日志系统进行重新初始化,重新加载日志系统配置文件,不修改日志文件的系统变量
protected void reinitialize(LoggingInitializationContext initializationContext) {
// 重置在日志上下文中保存的原有的信息
getLoggerContext().reset();
// 重置状态
getLoggerContext().getStatusManager().clear();
// 根据当前日志实现(Logback)获取到该实现的配置文件
String selfInitializationConfig = this.getSelfInitializationConfig();
// 重新加载配置文件
this.loadConfiguration(initializationContext, selfInitializationConfig, null);
}
// 获取日志上下文(创建日志的工厂)
private LoggerContext getLoggerContext() {
// 获取创建日志对象的工厂
ILoggerFactory factory = StaticLoggerBinder.getSingleton().getLoggerFactory();
// 断言ILoggerFactory必须为LoggerContext的实现
Assert.isInstanceOf(LoggerContext.class, factory,
() -> String.format(
"LoggerFactory is not a Logback LoggerContext but Logback is on "
+ "the classpath. Either remove Logback or the competing "
+ "implementation (%s loaded from %s). If you are using "
+ "WebLogic you will need to add 'org.slf4j' to "
+ "prefer-application-packages in WEB-INF/weblogic.xml",
factory.getClass(), getLocation(factory)));
// 返回该日志上下文(创建日志的工厂)
return (LoggerContext) factory;
}
}
SpringBoot实现日志收集原理
于 2024-04-19 02:54:09 首次发布