Add support for logging groups
Provide a way for users to quickly group related loggers together for easier configuration. The `loggers.group` property can be used to define a group that can then be configured in the usual `loggers.level.<group>` way. Additionally, provide pre-defined groups for `web` and `sql. Closes gh-14421
This commit is contained in:
parent
3ed9730b86
commit
b32887b8eb
|
|
@ -1731,6 +1731,46 @@ The following example shows potential logging settings in `application.propertie
|
|||
|
||||
|
||||
|
||||
[[boot-features-custom-log-groups]]
|
||||
=== Log Groups
|
||||
It's often useful to be able to group related loggers together so that they can all be
|
||||
configured at the same time. For example, you might commonly change the logging levels for
|
||||
_all_ Tomcat related loggers, but you can't easily remember to top level packages.
|
||||
|
||||
To help with this, Spring Boot allows you do define logging groups in your Spring
|
||||
`Environment`. For example, here's how you could define a "`tomcat`" group by adding
|
||||
it to your `appplication.properties`:
|
||||
|
||||
[source,properties,indent=0,subs="verbatim,quotes,attributes"]
|
||||
----
|
||||
logging.group.tomcat=org.apache.catalina, org.apache.coyote, org.apache.tomcat
|
||||
----
|
||||
|
||||
Once defined, you can change the level for all the loggers in the group with a single
|
||||
line:
|
||||
|
||||
[source,properties,indent=0,subs="verbatim,quotes,attributes"]
|
||||
----
|
||||
logging.level.tomcat=TRACE
|
||||
----
|
||||
|
||||
Spring Boot includes the following pre-defined logging groups that can be used
|
||||
out-of-the-box:
|
||||
|
||||
[cols="1,4"]
|
||||
|===
|
||||
|Name |Loggers
|
||||
|
||||
|web
|
||||
|`org.springframework.core.codec`, `org.springframework.http`, `org.springframework.web`
|
||||
|
||||
|sql
|
||||
|`org.springframework.jdbc.core`, `org.hibernate.SQL`
|
||||
|
||||
|===
|
||||
|
||||
|
||||
|
||||
[[boot-features-custom-log-configuration]]
|
||||
=== Custom Log Configuration
|
||||
The various logging systems can be activated by including the appropriate libraries on
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package org.springframework.boot.context.logging;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
|
@ -49,6 +50,7 @@ import org.springframework.core.env.ConfigurableEnvironment;
|
|||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
|
|
@ -56,7 +58,8 @@ import org.springframework.util.StringUtils;
|
|||
* An {@link ApplicationListener} that configures the {@link LoggingSystem}. If the
|
||||
* environment contains a {@code logging.config} property it will be used to bootstrap the
|
||||
* logging system, otherwise a default configuration is used. Regardless, logging levels
|
||||
* will be customized if the environment contains {@code logging.level.*} entries.
|
||||
* will be customized if the environment contains {@code logging.level.*} entries and
|
||||
* logging groups can be defined with {@code logging.group} .
|
||||
* <p>
|
||||
* Debug and trace logging for Spring, Tomcat, Jetty and Hibernate will be enabled when
|
||||
* the environment contains {@code debug} or {@code trace} properties that aren't set to
|
||||
|
|
@ -88,6 +91,9 @@ public class LoggingApplicationListener implements GenericApplicationListener {
|
|||
private static final Bindable<Map<String, String>> STRING_STRING_MAP = Bindable
|
||||
.mapOf(String.class, String.class);
|
||||
|
||||
private static final Bindable<Map<String, String[]>> STRING_STRINGS_MAP = Bindable
|
||||
.mapOf(String.class, String[].class);
|
||||
|
||||
/**
|
||||
* The default order for the LoggingApplicationListener.
|
||||
*/
|
||||
|
|
@ -111,19 +117,30 @@ public class LoggingApplicationListener implements GenericApplicationListener {
|
|||
*/
|
||||
public static final String LOGGING_SYSTEM_BEAN_NAME = "springBootLoggingSystem";
|
||||
|
||||
private static final MultiValueMap<LogLevel, String> LOG_LEVEL_LOGGERS;
|
||||
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("sql", "org.springframework.jdbc.core");
|
||||
loggers.add("sql", "org.hibernate.SQL");
|
||||
DEFAULT_GROUP_LOGGERS = Collections.unmodifiableMap(loggers);
|
||||
}
|
||||
|
||||
private static AtomicBoolean shutdownHookRegistered = new AtomicBoolean(false);
|
||||
private static final Map<LogLevel, List<String>> LOG_LEVEL_LOGGERS;
|
||||
|
||||
static {
|
||||
LOG_LEVEL_LOGGERS = new LinkedMultiValueMap<>();
|
||||
LOG_LEVEL_LOGGERS.add(LogLevel.DEBUG, "org.springframework.boot");
|
||||
LOG_LEVEL_LOGGERS.add(LogLevel.DEBUG, "org.hibernate.SQL");
|
||||
LOG_LEVEL_LOGGERS.add(LogLevel.TRACE, "org.springframework");
|
||||
LOG_LEVEL_LOGGERS.add(LogLevel.TRACE, "org.apache.tomcat");
|
||||
LOG_LEVEL_LOGGERS.add(LogLevel.TRACE, "org.apache.catalina");
|
||||
LOG_LEVEL_LOGGERS.add(LogLevel.TRACE, "org.eclipse.jetty");
|
||||
LOG_LEVEL_LOGGERS.add(LogLevel.TRACE, "org.hibernate.tool.hbm2ddl");
|
||||
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");
|
||||
LOG_LEVEL_LOGGERS = Collections.unmodifiableMap(loggers);
|
||||
}
|
||||
|
||||
private static final Class<?>[] EVENT_TYPES = { ApplicationStartingEvent.class,
|
||||
|
|
@ -133,6 +150,8 @@ public class LoggingApplicationListener implements GenericApplicationListener {
|
|||
private static final Class<?>[] SOURCE_TYPES = { SpringApplication.class,
|
||||
ApplicationContext.class };
|
||||
|
||||
private static AtomicBoolean shutdownHookRegistered = new AtomicBoolean(false);
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private LoggingSystem loggingSystem;
|
||||
|
|
@ -304,8 +323,32 @@ public class LoggingApplicationListener implements GenericApplicationListener {
|
|||
return;
|
||||
}
|
||||
Binder binder = Binder.get(environment);
|
||||
binder.bind("logging.level", STRING_STRING_MAP).orElseGet(Collections::emptyMap)
|
||||
.forEach((name, level) -> setLogLevel(system, name, level));
|
||||
Map<String, String[]> groups = getGroups(binder);
|
||||
Map<String, String> levels = binder.bind("logging.level", STRING_STRING_MAP)
|
||||
.orElseGet(Collections::emptyMap);
|
||||
levels.forEach((name, level) -> {
|
||||
String[] groupedNames = groups.get(name);
|
||||
if (ObjectUtils.isEmpty(groupedNames)) {
|
||||
setLogLevel(system, name, level);
|
||||
}
|
||||
else {
|
||||
setLogLevel(system, groupedNames, level);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Map<String, String[]> getGroups(Binder binder) {
|
||||
Map<String, String[]> groups = new LinkedHashMap<>();
|
||||
DEFAULT_GROUP_LOGGERS.forEach(
|
||||
(name, loggers) -> groups.put(name, StringUtils.toStringArray(loggers)));
|
||||
binder.bind("logging.group", STRING_STRINGS_MAP.withExistingValue(groups));
|
||||
return groups;
|
||||
}
|
||||
|
||||
private void setLogLevel(LoggingSystem system, String[] names, String level) {
|
||||
for (String name : names) {
|
||||
setLogLevel(system, name, level);
|
||||
}
|
||||
}
|
||||
|
||||
private void setLogLevel(LoggingSystem system, String name, String level) {
|
||||
|
|
@ -360,8 +403,9 @@ public class LoggingApplicationListener implements GenericApplicationListener {
|
|||
}
|
||||
|
||||
/**
|
||||
* Sets if initialization arguments should be parsed for {@literal --debug} and
|
||||
* {@literal --trace} options. Defaults to {@code true}.
|
||||
* Sets if initialization arguments should be parsed for {@literal debug} and
|
||||
* {@literal trace} properties (usually defined from {@literal --debug} or
|
||||
* {@literal --trace} command line args. Defaults to {@code true}.
|
||||
* @param parseArgs if arguments should be parsed
|
||||
*/
|
||||
public void setParseArgs(boolean parseArgs) {
|
||||
|
|
|
|||
|
|
@ -93,6 +93,12 @@
|
|||
"description": "Log levels severity mapping. For instance, `logging.level.org.springframework=DEBUG`.",
|
||||
"sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener"
|
||||
},
|
||||
{
|
||||
"name": "logging.group",
|
||||
"type": "java.util.Map<java.lang.String,java.lang.String>",
|
||||
"description": "Log groups to quickly change multipe loggers at the same time. For instance, `logging.level.db=org.hibernate,org.springframework.jdbc`.",
|
||||
"sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener"
|
||||
},
|
||||
{
|
||||
"name": "logging.path",
|
||||
"type": "java.lang.String",
|
||||
|
|
|
|||
|
|
@ -92,7 +92,9 @@ public class LoggingApplicationListenerTests {
|
|||
|
||||
private final LoggingApplicationListener initializer = new LoggingApplicationListener();
|
||||
|
||||
private final Log logger = new SLF4JLogFactory().getInstance(getClass());
|
||||
private final SLF4JLogFactory logFactory = new SLF4JLogFactory();
|
||||
|
||||
private final Log logger = this.logFactory.getInstance(getClass());
|
||||
|
||||
private final SpringApplication springApplication = new SpringApplication();
|
||||
|
||||
|
|
@ -558,6 +560,34 @@ public class LoggingApplicationListenerTests {
|
|||
assertThat(this.outputCapture.toString()).contains("testatdebug");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loggingGroupsDefaultsAreApplied() {
|
||||
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.context,
|
||||
"logging.level.web=TRACE");
|
||||
this.initializer.initialize(this.context.getEnvironment(),
|
||||
this.context.getClassLoader());
|
||||
assertTraceEnabled("org.springframework.core", false);
|
||||
assertTraceEnabled("org.springframework.core.codec", true);
|
||||
assertTraceEnabled("org.springframework.http", true);
|
||||
assertTraceEnabled("org.springframework.web", true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loggingGroupsCanBeDefined() {
|
||||
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.context,
|
||||
"logging.group.foo=com.foo.bar,com.foo.baz", "logging.level.foo=TRACE");
|
||||
this.initializer.initialize(this.context.getEnvironment(),
|
||||
this.context.getClassLoader());
|
||||
assertTraceEnabled("com.foo", false);
|
||||
assertTraceEnabled("com.foo.bar", true);
|
||||
assertTraceEnabled("com.foo.baz", true);
|
||||
}
|
||||
|
||||
private void assertTraceEnabled(String name, boolean expected) {
|
||||
assertThat(this.logFactory.getInstance(name).isTraceEnabled())
|
||||
.isEqualTo(expected);
|
||||
}
|
||||
|
||||
private void multicastEvent(ApplicationEvent event) {
|
||||
multicastEvent(this.initializer, event);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue