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]]
|
[[boot-features-custom-log-configuration]]
|
||||||
=== Custom Log Configuration
|
=== Custom Log Configuration
|
||||||
The various logging systems can be activated by including the appropriate libraries on
|
The various logging systems can be activated by including the appropriate libraries on
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package org.springframework.boot.context.logging;
|
package org.springframework.boot.context.logging;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
@ -49,6 +50,7 @@ import org.springframework.core.env.ConfigurableEnvironment;
|
||||||
import org.springframework.core.env.Environment;
|
import org.springframework.core.env.Environment;
|
||||||
import org.springframework.util.LinkedMultiValueMap;
|
import org.springframework.util.LinkedMultiValueMap;
|
||||||
import org.springframework.util.MultiValueMap;
|
import org.springframework.util.MultiValueMap;
|
||||||
|
import org.springframework.util.ObjectUtils;
|
||||||
import org.springframework.util.ResourceUtils;
|
import org.springframework.util.ResourceUtils;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
|
@ -56,7 +58,8 @@ import org.springframework.util.StringUtils;
|
||||||
* An {@link ApplicationListener} that configures the {@link LoggingSystem}. If the
|
* An {@link ApplicationListener} that configures the {@link LoggingSystem}. If the
|
||||||
* environment contains a {@code logging.config} property it will be used to bootstrap 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
|
* 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>
|
* <p>
|
||||||
* Debug and trace logging for Spring, Tomcat, Jetty and Hibernate will be enabled when
|
* 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
|
* 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
|
private static final Bindable<Map<String, String>> STRING_STRING_MAP = Bindable
|
||||||
.mapOf(String.class, String.class);
|
.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.
|
* The default order for the LoggingApplicationListener.
|
||||||
*/
|
*/
|
||||||
|
|
@ -111,19 +117,30 @@ public class LoggingApplicationListener implements GenericApplicationListener {
|
||||||
*/
|
*/
|
||||||
public static final String LOGGING_SYSTEM_BEAN_NAME = "springBootLoggingSystem";
|
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 {
|
static {
|
||||||
LOG_LEVEL_LOGGERS = new LinkedMultiValueMap<>();
|
MultiValueMap<LogLevel, String> loggers = new LinkedMultiValueMap<>();
|
||||||
LOG_LEVEL_LOGGERS.add(LogLevel.DEBUG, "org.springframework.boot");
|
loggers.add(LogLevel.DEBUG, "sql");
|
||||||
LOG_LEVEL_LOGGERS.add(LogLevel.DEBUG, "org.hibernate.SQL");
|
loggers.add(LogLevel.DEBUG, "web");
|
||||||
LOG_LEVEL_LOGGERS.add(LogLevel.TRACE, "org.springframework");
|
loggers.add(LogLevel.DEBUG, "org.springframework.boot");
|
||||||
LOG_LEVEL_LOGGERS.add(LogLevel.TRACE, "org.apache.tomcat");
|
loggers.add(LogLevel.TRACE, "org.springframework");
|
||||||
LOG_LEVEL_LOGGERS.add(LogLevel.TRACE, "org.apache.catalina");
|
loggers.add(LogLevel.TRACE, "org.apache.tomcat");
|
||||||
LOG_LEVEL_LOGGERS.add(LogLevel.TRACE, "org.eclipse.jetty");
|
loggers.add(LogLevel.TRACE, "org.apache.catalina");
|
||||||
LOG_LEVEL_LOGGERS.add(LogLevel.TRACE, "org.hibernate.tool.hbm2ddl");
|
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,
|
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,
|
private static final Class<?>[] SOURCE_TYPES = { SpringApplication.class,
|
||||||
ApplicationContext.class };
|
ApplicationContext.class };
|
||||||
|
|
||||||
|
private static AtomicBoolean shutdownHookRegistered = new AtomicBoolean(false);
|
||||||
|
|
||||||
private final Log logger = LogFactory.getLog(getClass());
|
private final Log logger = LogFactory.getLog(getClass());
|
||||||
|
|
||||||
private LoggingSystem loggingSystem;
|
private LoggingSystem loggingSystem;
|
||||||
|
|
@ -304,8 +323,32 @@ public class LoggingApplicationListener implements GenericApplicationListener {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Binder binder = Binder.get(environment);
|
Binder binder = Binder.get(environment);
|
||||||
binder.bind("logging.level", STRING_STRING_MAP).orElseGet(Collections::emptyMap)
|
Map<String, String[]> groups = getGroups(binder);
|
||||||
.forEach((name, level) -> setLogLevel(system, name, level));
|
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) {
|
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
|
* Sets if initialization arguments should be parsed for {@literal debug} and
|
||||||
* {@literal --trace} options. Defaults to {@code true}.
|
* {@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
|
* @param parseArgs if arguments should be parsed
|
||||||
*/
|
*/
|
||||||
public void setParseArgs(boolean parseArgs) {
|
public void setParseArgs(boolean parseArgs) {
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,12 @@
|
||||||
"description": "Log levels severity mapping. For instance, `logging.level.org.springframework=DEBUG`.",
|
"description": "Log levels severity mapping. For instance, `logging.level.org.springframework=DEBUG`.",
|
||||||
"sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener"
|
"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",
|
"name": "logging.path",
|
||||||
"type": "java.lang.String",
|
"type": "java.lang.String",
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,9 @@ public class LoggingApplicationListenerTests {
|
||||||
|
|
||||||
private final LoggingApplicationListener initializer = new LoggingApplicationListener();
|
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();
|
private final SpringApplication springApplication = new SpringApplication();
|
||||||
|
|
||||||
|
|
@ -558,6 +560,34 @@ public class LoggingApplicationListenerTests {
|
||||||
assertThat(this.outputCapture.toString()).contains("testatdebug");
|
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) {
|
private void multicastEvent(ApplicationEvent event) {
|
||||||
multicastEvent(this.initializer, event);
|
multicastEvent(this.initializer, event);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue