序
本文主要研究一下springboot的logging.group
LoggersEndpoint
org/springframework/boot/actuate/logging/LoggersEndpoint.java
代码语言:javascript复制@Endpoint(id = "loggers")
public class LoggersEndpoint {
private final LoggingSystem loggingSystem;
private final LoggerGroups loggerGroups;
/**
* Create a new {@link LoggersEndpoint} instance.
* @param loggingSystem the logging system to expose
* @param loggerGroups the logger group to expose
*/
public LoggersEndpoint(LoggingSystem loggingSystem, LoggerGroups loggerGroups) {
Assert.notNull(loggingSystem, "LoggingSystem must not be null");
Assert.notNull(loggerGroups, "LoggerGroups must not be null");
this.loggingSystem = loggingSystem;
this.loggerGroups = loggerGroups;
}
@ReadOperation
public Map<String, Object> loggers() {
Collection<LoggerConfiguration> configurations = this.loggingSystem.getLoggerConfigurations();
if (configurations == null) {
return Collections.emptyMap();
}
Map<String, Object> result = new LinkedHashMap<>();
result.put("levels", getLevels());
result.put("loggers", getLoggers(configurations));
result.put("groups", getGroups());
return result;
}
private Map<String, LoggerLevels> getGroups() {
Map<String, LoggerLevels> groups = new LinkedHashMap<>();
this.loggerGroups.forEach((group) -> groups.put(group.getName(),
new GroupLoggerLevels(group.getConfiguredLevel(), group.getMembers())));
return groups;
}
@ReadOperation
public LoggerLevels loggerLevels(@Selector String name) {
Assert.notNull(name, "Name must not be null");
LoggerGroup group = this.loggerGroups.get(name);
if (group != null) {
return new GroupLoggerLevels(group.getConfiguredLevel(), group.getMembers());
}
LoggerConfiguration configuration = this.loggingSystem.getLoggerConfiguration(name);
return (configuration != null) ? new SingleLoggerLevels(configuration) : null;
}
@WriteOperation
public void configureLogLevel(@Selector String name, @Nullable LogLevel configuredLevel) {
Assert.notNull(name, "Name must not be empty");
LoggerGroup group = this.loggerGroups.get(name);
if (group != null && group.hasMembers()) {
group.configureLogLevel(configuredLevel, this.loggingSystem::setLogLevel);
return;
}
this.loggingSystem.setLogLevel(name, configuredLevel);
}
//......
}
LoggersEndpoint提供了loggers、loggerLevels、configureLogLevel方法
LoggerGroups
org/springframework/boot/logging/LoggerGroups.java
代码语言:javascript复制public final class LoggerGroups implements Iterable<LoggerGroup> {
private final Map<String, LoggerGroup> groups = new ConcurrentHashMap<>();
public LoggerGroups() {
}
public LoggerGroups(Map<String, List<String>> namesAndMembers) {
putAll(namesAndMembers);
}
public void putAll(Map<String, List<String>> namesAndMembers) {
namesAndMembers.forEach(this::put);
}
private void put(String name, List<String> members) {
put(new LoggerGroup(name, members));
}
private void put(LoggerGroup loggerGroup) {
this.groups.put(loggerGroup.getName(), loggerGroup);
}
public LoggerGroup get(String name) {
return this.groups.get(name);
}
@Override
public Iterator<LoggerGroup> iterator() {
return this.groups.values().iterator();
}
}
LoggerGroups实现了Iterable接口,其泛型为LoggerGroup
LoggerGroup
org/springframework/boot/logging/LoggerGroup.java
代码语言:javascript复制public final class LoggerGroup {
private final String name;
private final List<String> members;
private LogLevel configuredLevel;
LoggerGroup(String name, List<String> members) {
this.name = name;
this.members = Collections.unmodifiableList(new ArrayList<>(members));
}
public String getName() {
return this.name;
}
public List<String> getMembers() {
return this.members;
}
public boolean hasMembers() {
return !this.members.isEmpty();
}
public LogLevel getConfiguredLevel() {
return this.configuredLevel;
}
public void configureLogLevel(LogLevel level, BiConsumer<String, LogLevel> configurer) {
this.configuredLevel = level;
this.members.forEach((name) -> configurer.accept(name, level));
}
}
LoggerGroup定义了name、members、configuredLevel属性,其configureLogLevel会遍历members,通过configurer(
LoggingSystem接口定义了setLogLevel方法
)去变更level
LogbackLoggingSystem
org/springframework/boot/logging/logback/LogbackLoggingSystem.java
代码语言:javascript复制 @Override
public void setLogLevel(String loggerName, LogLevel level) {
ch.qos.logback.classic.Logger logger = getLogger(loggerName);
if (logger != null) {
logger.setLevel(LEVELS.convertSystemToNative(level));
}
}
LogbackLoggingSystem的setLogLevel委托给了logger.setLevel
setLevel
ch/qos/logback/classic/Logger.java
代码语言:javascript复制 public synchronized void setLevel(Level newLevel) {
if (level == newLevel) {
// nothing to do;
return;
}
if (newLevel == null && isRootLogger()) {
throw new IllegalArgumentException("The level of the root logger cannot be set to null");
}
level = newLevel;
if (newLevel == null) {
effectiveLevelInt = parent.effectiveLevelInt;
newLevel = parent.getEffectiveLevel();
} else {
effectiveLevelInt = newLevel.levelInt;
}
if (childrenList != null) {
int len = childrenList.size();
for (int i = 0; i < len; i ) {
Logger child = (Logger) childrenList.get(i);
// tell child to handle parent levelInt change
child.handleParentLevelChange(effectiveLevelInt);
}
}
// inform listeners
loggerContext.fireOnLevelChange(this, newLevel);
}
setLevel先判断当前level是否需要变更,不需要直接返回,之后变更effectiveLevelInt为newLevel.levelInt,若该logger有childrenList,则触发child.handleParentLevelChange(effectiveLevelInt),最后执行loggerContext.fireOnLevelChange(this, newLevel)。
LoggingApplicationListener
org/springframework/boot/context/logging/LoggingApplicationListener.java
代码语言:javascript复制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");
private static final Bindable<Map<String, LogLevel>> STRING_LOGLEVEL_MAP = Bindable.mapOf(String.class,
LogLevel.class);
private static final Bindable<Map<String, List<String>>> STRING_STRINGS_MAP = Bindable
.of(ResolvableType.forClassWithGenerics(MultiValueMap.class, String.class, String.class).asMap());
/**
* The default order for the LoggingApplicationListener.
*/
public static final int DEFAULT_ORDER = Ordered.HIGHEST_PRECEDENCE 20;
/**
* The name of the Spring property that contains a reference to the logging
* configuration to load.
*/
public static final String CONFIG_PROPERTY = "logging.config";
/**
* The name of the Spring property that controls the registration of a shutdown hook
* to shut down the logging system when the JVM exits.
* @see LoggingSystem#getShutdownHandler
*/
public static final String REGISTER_SHUTDOWN_HOOK_PROPERTY = "logging.register-shutdown-hook";
/**
* The name of the {@link LoggingSystem} bean.
*/
public static final String LOGGING_SYSTEM_BEAN_NAME = "springBootLoggingSystem";
/**
* The name of the {@link LogFile} bean.
* @since 2.2.0
*/
public static final String LOG_FILE_BEAN_NAME = "springBootLogFile";
/**
* The name of the{@link LoggerGroups} bean.
* @since 2.2.0
*/
public static final String LOGGER_GROUPS_BEAN_NAME = "springBootLoggerGroups";
private static final Map<String, List<String>> DEFAULT_GROUP_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);
}
private static final Map<LogLevel, List<String>> SPRING_BOOT_LOGGING_LOGGERS;
static {
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);
}
//......
}
LoggingApplicationListener继承了GenericApplicationListener,它定义了DEFAULT_GROUP_LOGGERS,默认定义了web、sql两个LoggerGroup
示例
代码语言:javascript复制{
"levels": [
"OFF",
"ERROR",
"WARN",
"INFO",
"DEBUG",
"TRACE"
],
"loggers": {
"ROOT": {
"configuredLevel": "INFO",
"effectiveLevel": "INFO"
},
"Validator": {
"configuredLevel": null,
"effectiveLevel": "INFO"
}
},
"groups": {
"web": {
"configuredLevel": null,
"members": [
"org.springframework.core.codec",
"org.springframework.http",
"org.springframework.web",
"org.springframework.boot.actuate.endpoint.web",
"org.springframework.boot.web.servlet.ServletContextInitializerBeans"
]
},
"sql": {
"configuredLevel": null,
"members": [
"org.springframework.jdbc.core",
"org.hibernate.SQL",
"org.jooq.tools.LoggerListener"
]
}
}
}
小结
springboot的LoggersEndpoint提供了loggers、loggerLevels、configureLogLevel方法;LoggingApplicationListener继承了GenericApplicationListener,它定义了DEFAULT_GROUP_LOGGERS,默认定义了web、sql两个LoggerGroup;configureLogLevel方法可以传group名,也可以传具体的logger名,如果是group,则会一次变更其所有members的level。