聊聊springboot的LogbackLoggingSystem
序
本文主要研究一下springboot的LogbackLoggingSystem
LoggingSystem
org/springframework/boot/logging/LoggingSystem.java
public abstract class LoggingSystem {public abstract void beforeInitialize();public void initialize(LoggingInitializationContext initializationContext, String configLocation, LogFile logFile) {}public void cleanUp() {}public Runnable getShutdownHandler() {return null;}public Set<LogLevel> getSupportedLogLevels() {return EnumSet.allOf(LogLevel.class);}public void setLogLevel(String loggerName, LogLevel level) {throw new UnsupportedOperationException("Unable to set log level");}public List<LoggerConfiguration> getLoggerConfigurations() {throw new UnsupportedOperationException("Unable to get logger configurations");}public LoggerConfiguration getLoggerConfiguration(String loggerName) {throw new UnsupportedOperationException("Unable to get logger configuration");}
}
LoggingSystem定义了beforeInitialize抽象方法,需要子类实现,,同时还提供了setLogLevel、getLoggerConfigurations、getLoggerConfiguration,默认是抛出UnsupportedOperationException
NoOpLoggingSystem
static class NoOpLoggingSystem extends LoggingSystem {@Overridepublic void beforeInitialize() {}@Overridepublic void setLogLevel(String loggerName, LogLevel level) {}@Overridepublic List<LoggerConfiguration> getLoggerConfigurations() {return Collections.emptyList();}@Overridepublic LoggerConfiguration getLoggerConfiguration(String loggerName) {return null;}}
NoOpLoggingSystem继承了LoggingSystem,其方法都是空操作
AbstractLoggingSystem
org/springframework/boot/logging/AbstractLoggingSystem.java
public abstract class AbstractLoggingSystem extends LoggingSystem {protected static final Comparator<LoggerConfiguration> CONFIGURATION_COMPARATOR = new LoggerConfigurationComparator(ROOT_LOGGER_NAME);private final ClassLoader classLoader;public AbstractLoggingSystem(ClassLoader classLoader) {this.classLoader = classLoader;}@Overridepublic void beforeInitialize() {}@Overridepublic void initialize(LoggingInitializationContext initializationContext, String configLocation, LogFile logFile) {if (StringUtils.hasLength(configLocation)) {initializeWithSpecificConfig(initializationContext, configLocation, logFile);return;}initializeWithConventions(initializationContext, logFile);}/*** Load sensible defaults for the logging system.* @param initializationContext the logging initialization context* @param logFile the file to load or {@code null} if no log file is to be written*/protected abstract void loadDefaults(LoggingInitializationContext initializationContext, LogFile logFile);/*** Load a specific configuration.* @param initializationContext the logging initialization context* @param location the location of the configuration to load (never {@code null})* @param logFile the file to load or {@code null} if no log file is to be written*/protected abstract void loadConfiguration(LoggingInitializationContext initializationContext, String location,LogFile logFile);/*** Reinitialize the logging system if required. Called when* {@link #getSelfInitializationConfig()} is used and the log file hasn't changed. May* be used to reload configuration (for example to pick up additional System* properties).* @param initializationContext the logging initialization context*/protected void reinitialize(LoggingInitializationContext initializationContext) {}//......
}
AbstractLoggingSystem继承了LoggingSystem,它主要是重写了initialize方法,若存在configLocation配置则执行initializeWithSpecificConfig,否则执行initializeWithConventions;它同时还定义了loadDefaults、loadConfiguration抽象方法需要子类实现
Slf4JLoggingSystem
org/springframework/boot/logging/Slf4JLoggingSystem.java
public abstract class Slf4JLoggingSystem extends AbstractLoggingSystem {private static final String BRIDGE_HANDLER = "org.slf4j.bridge.SLF4JBridgeHandler";public Slf4JLoggingSystem(ClassLoader classLoader) {super(classLoader);}@Overridepublic void beforeInitialize() {super.beforeInitialize();configureJdkLoggingBridgeHandler();}@Overridepublic void cleanUp() {if (isBridgeHandlerAvailable()) {removeJdkLoggingBridgeHandler();}}@Overrideprotected void loadConfiguration(LoggingInitializationContext initializationContext, String location,LogFile logFile) {Assert.notNull(location, "Location must not be null");if (initializationContext != null) {applySystemProperties(initializationContext.getEnvironment(), logFile);}}//......
}
Slf4JLoggingSystem继承了AbstractLoggingSystem,它覆盖了beforeInitialize,新增configureJdkLoggingBridgeHandler;其cleanUp方法在isBridgeHandlerAvailable的时候执行removeJdkLoggingBridgeHandler;其loadConfiguration在initializationContext不为null的时候执行applySystemProperties
configureJdkLoggingBridgeHandler
private void configureJdkLoggingBridgeHandler() {try {if (isBridgeJulIntoSlf4j()) {removeJdkLoggingBridgeHandler();SLF4JBridgeHandler.install();}}catch (Throwable ex) {// Ignore. No java.util.logging bridge is installed.}}
configureJdkLoggingBridgeHandler主要是判断是否将JUL绑定到SLF4J,是的话则removeJdkLoggingBridgeHandler,然后执行SLF4JBridgeHandler.install()
removeJdkLoggingBridgeHandler
private void removeJdkLoggingBridgeHandler() {try {removeDefaultRootHandler();SLF4JBridgeHandler.uninstall();}catch (Throwable ex) {// Ignore and continue}}private void removeDefaultRootHandler() {try {Logger rootLogger = LogManager.getLogManager().getLogger("");Handler[] handlers = rootLogger.getHandlers();if (handlers.length == 1 && handlers[0] instanceof ConsoleHandler) {rootLogger.removeHandler(handlers[0]);}}catch (Throwable ex) {// Ignore and continue}}
removeJdkLoggingBridgeHandler主要是执行removeDefaultRootHandler,以及SLF4JBridgeHandler.uninstall()
applySystemProperties
protected final void applySystemProperties(Environment environment, LogFile logFile) {new LoggingSystemProperties(environment).apply(logFile);}public void apply(LogFile logFile) {PropertyResolver resolver = getPropertyResolver();setSystemProperty(resolver, EXCEPTION_CONVERSION_WORD, "exception-conversion-word");setSystemProperty(PID_KEY, new ApplicationPid().toString());setSystemProperty(resolver, CONSOLE_LOG_PATTERN, "pattern.console");setSystemProperty(resolver, FILE_LOG_PATTERN, "pattern.file");setSystemProperty(resolver, FILE_CLEAN_HISTORY_ON_START, "file.clean-history-on-start");setSystemProperty(resolver, FILE_MAX_HISTORY, "file.max-history");setSystemProperty(resolver, FILE_MAX_SIZE, "file.max-size");setSystemProperty(resolver, FILE_TOTAL_SIZE_CAP, "file.total-size-cap");setSystemProperty(resolver, LOG_LEVEL_PATTERN, "pattern.level");setSystemProperty(resolver, LOG_DATEFORMAT_PATTERN, "pattern.dateformat");setSystemProperty(resolver, ROLLING_FILE_NAME_PATTERN, "pattern.rolling-file-name");if (logFile != null) {logFile.applyToSystemProperties();}}
applySystemProperties通过LoggingSystemProperties设置了系统属性方便后续log配置文件使用
LogbackLoggingSystem
org/springframework/boot/logging/logback/LogbackLoggingSystem.java
public class LogbackLoggingSystem extends Slf4JLoggingSystem {private static final String CONFIGURATION_FILE_PROPERTY = "logback.configurationFile";private static final LogLevels<Level> LEVELS = new LogLevels<>();static {LEVELS.map(LogLevel.TRACE, Level.TRACE);LEVELS.map(LogLevel.TRACE, Level.ALL);LEVELS.map(LogLevel.DEBUG, Level.DEBUG);LEVELS.map(LogLevel.INFO, Level.INFO);LEVELS.map(LogLevel.WARN, Level.WARN);LEVELS.map(LogLevel.ERROR, Level.ERROR);LEVELS.map(LogLevel.FATAL, Level.ERROR);LEVELS.map(LogLevel.OFF, Level.OFF);}private static final TurboFilter FILTER = new TurboFilter() {@Overridepublic FilterReply decide(Marker marker, ch.qos.logback.classic.Logger logger, Level level, String format,Object[] params, Throwable t) {return FilterReply.DENY;}};public LogbackLoggingSystem(ClassLoader classLoader) {super(classLoader);}@Overrideprotected String[] getStandardConfigLocations() {return new String[] { "logback-test.groovy", "logback-test.xml", "logback.groovy", "logback.xml" };}//......
}
LogbackLoggingSystem继承了Slf4JLoggingSystem,其getStandardConfigLocations返回
logback-test.groovy, logback-test.xml, logback.groovy, logback.xml
beforeInitialize
@Overridepublic void beforeInitialize() {LoggerContext loggerContext = getLoggerContext();if (isAlreadyInitialized(loggerContext)) {return;}super.beforeInitialize();loggerContext.getTurboFilterList().add(FILTER);}
beforeInitialize方法主要是添加了TurboFilter
initialize
@Overridepublic 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.");}}
initialize方法执行super.initialize(initializationContext, configLocation, logFile),然后markAsInitialized(loggerContext)
loadDefaults
@Overrideprotected void loadDefaults(LoggingInitializationContext initializationContext, LogFile logFile) {LoggerContext context = getLoggerContext();stopAndReset(context);boolean debug = Boolean.getBoolean("logback.debug");if (debug) {StatusListenerConfigHelper.addOnConsoleListenerInstance(context, new OnConsoleStatusListener());}LogbackConfigurator configurator = debug ? new DebugLogbackConfigurator(context): new LogbackConfigurator(context);Environment environment = initializationContext.getEnvironment();context.putProperty(LoggingSystemProperties.LOG_LEVEL_PATTERN,environment.resolvePlaceholders("${logging.pattern.level:${LOG_LEVEL_PATTERN:%5p}}"));context.putProperty(LoggingSystemProperties.LOG_DATEFORMAT_PATTERN, environment.resolvePlaceholders("${logging.pattern.dateformat:${LOG_DATEFORMAT_PATTERN:yyyy-MM-dd HH:mm:ss.SSS}}"));context.putProperty(LoggingSystemProperties.ROLLING_FILE_NAME_PATTERN, environment.resolvePlaceholders("${logging.pattern.rolling-file-name:${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz}"));new DefaultLogbackConfiguration(initializationContext, logFile).apply(configurator);context.setPackagingDataEnabled(true);}
loadDefaults方法通过LOG_LEVEL_PATTERN、LOG_DATEFORMAT_PATTERN、ROLLING_FILE_NAME_PATTERN以及LogbackConfigurator来初始化DefaultLogbackConfiguration
loadConfiguration
@Overrideprotected 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 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 {new ContextInitializer(loggerContext).configureByResource(url);}}
loadConfiguration方法主要是执行configureByResourceUrl,该方法通过SpringBootJoranConfigurator或者ContextInitializer的configureByResource进行配置
小结
springboot定义了LoggingSystem、AbstractLoggingSystem、Slf4JLoggingSystem,依次继承,而LogbackLoggingSystem则继承Slf4JLoggingSystem,它主要是定义了要加载的默认的配置文件logback-test.groovy, logback-test.xml, logback.groovy, logback.xml
,以及loadDefaults方法,通过LOG_LEVEL_PATTERN、LOG_DATEFORMAT_PATTERN、ROLLING_FILE_NAME_PATTERN以及LogbackConfigurator来初始化DefaultLogbackConfiguration。