Rationalize Logback logging properties

Deprecate and provide alternatives for logging properties that are
specific to Logback.

The following Spring Boot properties have been changed:

  * logging.pattern.rolling-file-name ->
    logging.logback.rollingpolicy.file-name-pattern

  * logging.file.clean-history-on-start ->
    logging.logback.rollingpolicy.clean-history-on-start

  * logging.file.max-size ->
    logging.logback.rollingpolicy.max-file-size

  * logging.file.total-size-cap ->
    logging.logback.rollingpolicy.total-size-cap

  * logging.file.max-history ->
    logging.logback.rollingpolicy.max-history

As have the system environment properties that they map to:

  * ROLLING_FILE_NAME_PATTERN ->
    LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN

  * LOG_FILE_CLEAN_HISTORY_ON_START ->
    LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START

  * LOG_FILE_MAX_SIZE ->
    LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE

  * LOG_FILE_TOTAL_SIZE_CAP ->
    LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP

  * LOG_FILE_MAX_HISTORY ->
    LOGBACK_ROLLINGPOLICY_MAX_HISTORY

This commit also cleans up and simplifies `DefaultLogbackConfiguration`.

Closes gh-23609
pull/23886/head
Phillip Webb 4 years ago
parent 9c54a5369d
commit 1725594a0e

@ -2040,17 +2040,40 @@ The following table shows how the `logging.*` properties can be used together:
|=== |===
Log files rotate when they reach 10 MB and, as with console output, `ERROR`-level, `WARN`-level, and `INFO`-level messages are logged by default. Log files rotate when they reach 10 MB and, as with console output, `ERROR`-level, `WARN`-level, and `INFO`-level messages are logged by default.
Size limits can be changed using the configprop:logging.file.max-size[] property.
Rotated log files of the last 7 days are kept by default unless the configprop:logging.file.max-history[] property has been set.
The total size of log archives can be capped using configprop:logging.file.total-size-cap[].
When the total size of log archives exceeds that threshold, backups will be deleted.
To force log archive cleanup on application startup, use the configprop:logging.file.clean-history-on-start[] property.
TIP: Logging properties are independent of the actual logging infrastructure. TIP: Logging properties are independent of the actual logging infrastructure.
As a result, specific configuration keys (such as `logback.configurationFile` for Logback) are not managed by spring Boot. As a result, specific configuration keys (such as `logback.configurationFile` for Logback) are not managed by spring Boot.
[[boot-features-logging-file-rotation]]
=== File Rotation
If you are using the Logback, it's possible to fine-tune log rotation settings using your `application.properties` or `application.yaml` file.
For all other logging system, you'll need to configure rotation settings directly yourself (for example, if you use Log4J2 then you could add a `log4j.xml` file).
The following rotation policy properties are supported:
|===
| Name | Description
| configprop:logging.logback.rollingpolicy.file-name-pattern[]
| The filename pattern used to create log archives.
| configprop:logging.logback.rollingpolicy.clean-history-on-start[]
| If log archive cleanup should occur when the application starts.
| configprop:logging.logback.rollingpolicy.max-file-size[]
| The maximum size of log file before it's archived.
| configprop:logging.logback.rollingpolicy.total-size-cap[]
| The maximum amount of size log archives can take before being deleted.
| configprop:logging.logback.rollingpolicy.max-history[]
| The number of days to keep log archives (defaults to 7)
|===
[[boot-features-custom-log-levels]] [[boot-features-custom-log-levels]]
=== Log Levels === Log Levels
All the supported logging systems can have the logger levels set in the Spring `Environment` (for example, in `application.properties`) by using `+logging.level.<logger-name>=<level>+` where `level` is one of TRACE, DEBUG, INFO, WARN, ERROR, FATAL, or OFF. All the supported logging systems can have the logger levels set in the Spring `Environment` (for example, in `application.properties`) by using `+logging.level.<logger-name>=<level>+` where `level` is one of TRACE, DEBUG, INFO, WARN, ERROR, FATAL, or OFF.
@ -2165,64 +2188,62 @@ To help with the customization, some other properties are transferred from the S
| `LOG_EXCEPTION_CONVERSION_WORD` | `LOG_EXCEPTION_CONVERSION_WORD`
| The conversion word used when logging exceptions. | The conversion word used when logging exceptions.
| configprop:logging.file.clean-history-on-start[]
| `LOG_FILE_CLEAN_HISTORY_ON_START`
| Whether to clean the archive log files on startup (if LOG_FILE enabled).
(Only supported with the default Logback setup.)
| configprop:logging.file.name[] | configprop:logging.file.name[]
| `LOG_FILE` | `LOG_FILE`
| If defined, it is used in the default log configuration. | If defined, it is used in the default log configuration.
| configprop:logging.file.max-size[]
| `LOG_FILE_MAX_SIZE`
| Maximum log file size (if LOG_FILE enabled).
(Only supported with the default Logback setup.)
| configprop:logging.file.max-history[]
| `LOG_FILE_MAX_HISTORY`
| Maximum number of archive log files to keep (if LOG_FILE enabled).
(Only supported with the default Logback setup.)
| configprop:logging.file.path[] | configprop:logging.file.path[]
| `LOG_PATH` | `LOG_PATH`
| If defined, it is used in the default log configuration. | If defined, it is used in the default log configuration.
| configprop:logging.file.total-size-cap[]
| `LOG_FILE_TOTAL_SIZE_CAP`
| Total size of log backups to be kept (if LOG_FILE enabled).
(Only supported with the default Logback setup.)
| configprop:logging.pattern.console[] | configprop:logging.pattern.console[]
| `CONSOLE_LOG_PATTERN` | `CONSOLE_LOG_PATTERN`
| The log pattern to use on the console (stdout). | The log pattern to use on the console (stdout).
(Only supported with the default Logback setup.)
| configprop:logging.pattern.dateformat[] | configprop:logging.pattern.dateformat[]
| `LOG_DATEFORMAT_PATTERN` | `LOG_DATEFORMAT_PATTERN`
| Appender pattern for log date format. | Appender pattern for log date format.
(Only supported with the default Logback setup.)
| configprop:logging.pattern.file[] | configprop:logging.pattern.file[]
| `FILE_LOG_PATTERN` | `FILE_LOG_PATTERN`
| The log pattern to use in a file (if `LOG_FILE` is enabled). | The log pattern to use in a file (if `LOG_FILE` is enabled).
(Only supported with the default Logback setup.)
| configprop:logging.pattern.level[] | configprop:logging.pattern.level[]
| `LOG_LEVEL_PATTERN` | `LOG_LEVEL_PATTERN`
| The format to use when rendering the log level (default `%5p`). | The format to use when rendering the log level (default `%5p`).
(Only supported with the default Logback setup.)
| configprop:logging.pattern.rolling-file-name[]
| `ROLLING_FILE_NAME_PATTERN`
| Pattern for rolled-over log file names (default `$\{LOG_FILE}.%d\{yyyy-MM-dd}.%i.gz`).
(Only supported with the default Logback setup.)
| `PID` | `PID`
| `PID` | `PID`
| The current process ID (discovered if possible and when not already defined as an OS environment variable). | The current process ID (discovered if possible and when not already defined as an OS environment variable).
|=== |===
If you're using Logback, the following properties are also transfered:
|===
| Spring Environment | System Property | Comments
| configprop:logging.logback.rollingpolicy.file-name-pattern[]
| `LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN`
| Pattern for rolled-over log file names (default `$\{LOG_FILE}.%d\{yyyy-MM-dd}.%i.gz`).
| configprop:logging.logback.rollingpolicy.clean-history-on-start[]
| `LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START`
| Whether to clean the archive log files on startup.
| configprop:logging.logback.rollingpolicy.max-file-size[]
| `LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE`
| Maximum log file size.
| configprop:logging.logback.rollingpolicy.total-size-cap[]
| `LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP`
| Total size of log backups to be kept.
| configprop:logging.logback.rollingpolicy.max-history[]
| `LOGBACK_ROLLINGPOLICY_MAX_HISTORY`
| Maximum number of archive log files to keep.
|===
All the supported logging systems can consult System properties when parsing their configuration files. All the supported logging systems can consult System properties when parsing their configuration files.
See the default configurations in `spring-boot.jar` for examples: See the default configurations in `spring-boot.jar` for examples:

@ -271,7 +271,7 @@ public class LoggingApplicationListener implements GenericApplicationListener {
* @param classLoader the classloader * @param classLoader the classloader
*/ */
protected void initialize(ConfigurableEnvironment environment, ClassLoader classLoader) { protected void initialize(ConfigurableEnvironment environment, ClassLoader classLoader) {
new LoggingSystemProperties(environment).apply(); getLoggingSystemProperties(environment).apply();
this.logFile = LogFile.get(environment); this.logFile = LogFile.get(environment);
if (this.logFile != null) { if (this.logFile != null) {
this.logFile.applyToSystemProperties(); this.logFile.applyToSystemProperties();
@ -283,6 +283,11 @@ public class LoggingApplicationListener implements GenericApplicationListener {
registerShutdownHookIfNecessary(environment, this.loggingSystem); registerShutdownHookIfNecessary(environment, this.loggingSystem);
} }
private LoggingSystemProperties getLoggingSystemProperties(ConfigurableEnvironment environment) {
return (this.loggingSystem != null) ? this.loggingSystem.getSystemProperties(environment)
: new LoggingSystemProperties(environment);
}
private void initializeEarlyLoggingLevel(ConfigurableEnvironment environment) { private void initializeEarlyLoggingLevel(ConfigurableEnvironment environment) {
if (this.parseArgs && this.springBootLogging == null) { if (this.parseArgs && this.springBootLogging == null) {
if (isSet(environment, "debug")) { if (isSet(environment, "debug")) {

@ -22,6 +22,7 @@ import java.util.EnumSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@ -57,6 +58,16 @@ public abstract class LoggingSystem {
private static final LoggingSystemFactory SYSTEM_FACTORY = LoggingSystemFactory.fromSpringFactories(); private static final LoggingSystemFactory SYSTEM_FACTORY = LoggingSystemFactory.fromSpringFactories();
/**
* Return the {@link LoggingSystemProperties} that should be applied.
* @param environment the {@link ConfigurableEnvironment} used to obtain value
* @return the {@link LoggingSystemProperties} to apply
* @since 2.4.0
*/
public LoggingSystemProperties getSystemProperties(ConfigurableEnvironment environment) {
return new LoggingSystemProperties(environment);
}
/** /**
* Reset the logging system to be limit output. This method may be called before * Reset the logging system to be limit output. This method may be called before
* {@link #initialize(LoggingInitializationContext, String, LogFile)} to reduce * {@link #initialize(LoggingInitializationContext, String, LogFile)} to reduce

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2019 the original author or authors. * Copyright 2012-2020 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -62,30 +62,51 @@ public class LoggingSystemProperties {
public static final String CONSOLE_LOG_PATTERN = "CONSOLE_LOG_PATTERN"; public static final String CONSOLE_LOG_PATTERN = "CONSOLE_LOG_PATTERN";
/** /**
* The name of the System property that contains the clean history on start flag. * The name of the System property that contains the file log pattern.
*/ */
public static final String FILE_CLEAN_HISTORY_ON_START = "LOG_FILE_CLEAN_HISTORY_ON_START"; public static final String FILE_LOG_PATTERN = "FILE_LOG_PATTERN";
/** /**
* The name of the System property that contains the file log pattern. * The name of the System property that contains the rolled-over log file name
* pattern.
* @deprecated since 2.4.0 in favor of
* {@link org.springframework.boot.logging.logback.LogbackLoggingSystemProperties#ROLLINGPOLICY_FILE_NAME_PATTERN}
*/ */
public static final String FILE_LOG_PATTERN = "FILE_LOG_PATTERN"; @Deprecated
public static final String ROLLING_FILE_NAME_PATTERN = "ROLLING_FILE_NAME_PATTERN";
/** /**
* The name of the System property that contains the file log max history. * The name of the System property that contains the clean history on start flag.
* @deprecated since 2.4.0 in favor of
* {@link org.springframework.boot.logging.logback.LogbackLoggingSystemProperties#ROLLINGPOLICY_CLEAN_HISTORY_ON_START}
*/ */
public static final String FILE_MAX_HISTORY = "LOG_FILE_MAX_HISTORY"; @Deprecated
public static final String FILE_CLEAN_HISTORY_ON_START = "LOG_FILE_CLEAN_HISTORY_ON_START";
/** /**
* The name of the System property that contains the file log max size. * The name of the System property that contains the file log max size.
* @deprecated since 2.4.0 in favor of
* {@link org.springframework.boot.logging.logback.LogbackLoggingSystemProperties#ROLLINGPOLICY_MAX_FILE_SIZE}
*/ */
@Deprecated
public static final String FILE_MAX_SIZE = "LOG_FILE_MAX_SIZE"; public static final String FILE_MAX_SIZE = "LOG_FILE_MAX_SIZE";
/** /**
* The name of the System property that contains the file total size cap. * The name of the System property that contains the file total size cap.
* @deprecated since 2.4.0 in favor of
* {@link org.springframework.boot.logging.logback.LogbackLoggingSystemProperties#ROLLINGPOLICY_TOTAL_SIZE_CAP}
*/ */
@Deprecated
public static final String FILE_TOTAL_SIZE_CAP = "LOG_FILE_TOTAL_SIZE_CAP"; public static final String FILE_TOTAL_SIZE_CAP = "LOG_FILE_TOTAL_SIZE_CAP";
/**
* The name of the System property that contains the file log max history.
* @deprecated since 2.4.0 in favor of
* {@link org.springframework.boot.logging.logback.LogbackLoggingSystemProperties#ROLLINGPOLICY_MAX_HISTORY}
*/
@Deprecated
public static final String FILE_MAX_HISTORY = "LOG_FILE_MAX_HISTORY";
/** /**
* The name of the System property that contains the log level pattern. * The name of the System property that contains the log level pattern.
*/ */
@ -96,12 +117,6 @@ public class LoggingSystemProperties {
*/ */
public static final String LOG_DATEFORMAT_PATTERN = "LOG_DATEFORMAT_PATTERN"; public static final String LOG_DATEFORMAT_PATTERN = "LOG_DATEFORMAT_PATTERN";
/**
* The name of the System property that contains the rolled-over log file name
* pattern.
*/
public static final String ROLLING_FILE_NAME_PATTERN = "ROLLING_FILE_NAME_PATTERN";
private final Environment environment; private final Environment environment;
/** /**
@ -113,43 +128,52 @@ public class LoggingSystemProperties {
this.environment = environment; this.environment = environment;
} }
public void apply() { public final void apply() {
apply(null); apply(null);
} }
public void apply(LogFile logFile) { public final void apply(LogFile logFile) {
PropertyResolver resolver = getPropertyResolver(); PropertyResolver resolver = getPropertyResolver();
setSystemProperty(resolver, EXCEPTION_CONVERSION_WORD, "exception-conversion-word"); apply(logFile, resolver);
}
protected void apply(LogFile logFile, PropertyResolver resolver) {
setSystemProperty(resolver, EXCEPTION_CONVERSION_WORD, "logging.exception-conversion-word");
setSystemProperty(PID_KEY, new ApplicationPid().toString()); setSystemProperty(PID_KEY, new ApplicationPid().toString());
setSystemProperty(resolver, CONSOLE_LOG_PATTERN, "pattern.console"); setSystemProperty(resolver, CONSOLE_LOG_PATTERN, "logging.pattern.console");
setSystemProperty(resolver, FILE_LOG_PATTERN, "pattern.file"); setSystemProperty(resolver, LOG_DATEFORMAT_PATTERN, "logging.pattern.dateformat");
setSystemProperty(resolver, FILE_CLEAN_HISTORY_ON_START, "file.clean-history-on-start"); setSystemProperty(resolver, FILE_LOG_PATTERN, "logging.pattern.file");
setSystemProperty(resolver, FILE_MAX_HISTORY, "file.max-history"); setSystemProperty(resolver, LOG_LEVEL_PATTERN, "logging.pattern.level");
setSystemProperty(resolver, FILE_MAX_SIZE, "file.max-size"); applyDeprecated(resolver);
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) { if (logFile != null) {
logFile.applyToSystemProperties(); logFile.applyToSystemProperties();
} }
} }
private void applyDeprecated(PropertyResolver resolver) {
setSystemProperty(resolver, FILE_CLEAN_HISTORY_ON_START, "logging.file.clean-history-on-start");
setSystemProperty(resolver, FILE_MAX_HISTORY, "logging.file.max-history");
setSystemProperty(resolver, FILE_MAX_SIZE, "logging.file.max-size");
setSystemProperty(resolver, FILE_TOTAL_SIZE_CAP, "logging.file.total-size-cap");
setSystemProperty(resolver, ROLLING_FILE_NAME_PATTERN, "logging.pattern.rolling-file-name");
}
private PropertyResolver getPropertyResolver() { private PropertyResolver getPropertyResolver() {
if (this.environment instanceof ConfigurableEnvironment) { if (this.environment instanceof ConfigurableEnvironment) {
PropertySourcesPropertyResolver resolver = new PropertySourcesPropertyResolver( PropertySourcesPropertyResolver resolver = new PropertySourcesPropertyResolver(
((ConfigurableEnvironment) this.environment).getPropertySources()); ((ConfigurableEnvironment) this.environment).getPropertySources());
resolver.setConversionService(((ConfigurableEnvironment) this.environment).getConversionService());
resolver.setIgnoreUnresolvableNestedPlaceholders(true); resolver.setIgnoreUnresolvableNestedPlaceholders(true);
return resolver; return resolver;
} }
return this.environment; return this.environment;
} }
private void setSystemProperty(PropertyResolver resolver, String systemPropertyName, String propertyName) { protected final void setSystemProperty(PropertyResolver resolver, String systemPropertyName, String propertyName) {
setSystemProperty(systemPropertyName, resolver.getProperty("logging." + propertyName)); setSystemProperty(systemPropertyName, resolver.getProperty(propertyName));
} }
private void setSystemProperty(String name, String value) { protected final void setSystemProperty(String name, String value) {
if (System.getProperty(name) == null && value != null) { if (System.getProperty(name) == null && value != null) {
System.setProperty(name, value); System.setProperty(name, value);
} }

@ -16,14 +16,11 @@
package org.springframework.boot.logging.logback; package org.springframework.boot.logging.logback;
import java.lang.reflect.Method;
import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder; import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender; import ch.qos.logback.core.Appender;
import ch.qos.logback.core.ConsoleAppender; import ch.qos.logback.core.ConsoleAppender;
import ch.qos.logback.core.CoreConstants;
import ch.qos.logback.core.rolling.RollingFileAppender; import ch.qos.logback.core.rolling.RollingFileAppender;
import ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy; import ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy;
import ch.qos.logback.core.util.FileSize; import ch.qos.logback.core.util.FileSize;
@ -31,17 +28,12 @@ import ch.qos.logback.core.util.OptionHelper;
import org.springframework.boot.logging.LogFile; import org.springframework.boot.logging.LogFile;
import org.springframework.boot.logging.LoggingInitializationContext; import org.springframework.boot.logging.LoggingInitializationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertyResolver;
import org.springframework.core.env.PropertySourcesPropertyResolver;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.unit.DataSize;
/** /**
* Default logback configuration used by Spring Boot. Uses {@link LogbackConfigurator} to * Default logback configuration used by Spring Boot. Uses {@link LogbackConfigurator} to
* improve startup time. See also the {@code defaults.xml}, {@code console-appender.xml} * improve startup time. See also the {@code base.xml}, {@code defaults.xml},
* and {@code file-appender.xml} files provided for classic {@code logback.xml} use. * {@code console-appender.xml} and {@code file-appender.xml} files provided for classic
* {@code logback.xml} use.
* *
* @author Phillip Webb * @author Phillip Webb
* @author Madhura Bhave * @author Madhura Bhave
@ -50,43 +42,15 @@ import org.springframework.util.unit.DataSize;
*/ */
class DefaultLogbackConfiguration { class DefaultLogbackConfiguration {
private static final String CONSOLE_LOG_PATTERN = "%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} "
+ "%clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} "
+ "%clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} "
+ "%clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}";
private static final String FILE_LOG_PATTERN = "%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} "
+ "${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- [%t] %-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}";
private static final DataSize MAX_FILE_SIZE = DataSize.ofMegabytes(10);
private static final Integer MAX_FILE_HISTORY = 7;
private final PropertyResolver patterns;
private final LogFile logFile; private final LogFile logFile;
DefaultLogbackConfiguration(LoggingInitializationContext initializationContext, LogFile logFile) { DefaultLogbackConfiguration(LoggingInitializationContext initializationContext, LogFile logFile) {
this.patterns = getPatternsResolver(initializationContext.getEnvironment());
this.logFile = logFile; this.logFile = logFile;
} }
private PropertyResolver getPatternsResolver(Environment environment) {
if (environment == null) {
return new PropertySourcesPropertyResolver(null);
}
if (environment instanceof ConfigurableEnvironment) {
PropertySourcesPropertyResolver resolver = new PropertySourcesPropertyResolver(
((ConfigurableEnvironment) environment).getPropertySources());
resolver.setIgnoreUnresolvableNestedPlaceholders(true);
return resolver;
}
return environment;
}
void apply(LogbackConfigurator config) { void apply(LogbackConfigurator config) {
synchronized (config.getConfigurationLock()) { synchronized (config.getConfigurationLock()) {
base(config); defaults(config);
Appender<ILoggingEvent> consoleAppender = consoleAppender(config); Appender<ILoggingEvent> consoleAppender = consoleAppender(config);
if (this.logFile != null) { if (this.logFile != null) {
Appender<ILoggingEvent> fileAppender = fileAppender(config, this.logFile.toString()); Appender<ILoggingEvent> fileAppender = fileAppender(config, this.logFile.toString());
@ -98,10 +62,17 @@ class DefaultLogbackConfiguration {
} }
} }
private void base(LogbackConfigurator config) { private void defaults(LogbackConfigurator config) {
config.conversionRule("clr", ColorConverter.class); config.conversionRule("clr", ColorConverter.class);
config.conversionRule("wex", WhitespaceThrowableProxyConverter.class); config.conversionRule("wex", WhitespaceThrowableProxyConverter.class);
config.conversionRule("wEx", ExtendedWhitespaceThrowableProxyConverter.class); config.conversionRule("wEx", ExtendedWhitespaceThrowableProxyConverter.class);
config.getContext().putProperty("CONSOLE_LOG_PATTERN", resolve(config, "${CONSOLE_LOG_PATTERN:-"
+ "%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) "
+ "%clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} "
+ "%clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"));
config.getContext().putProperty("FILE_LOG_PATTERN", resolve(config, "${FILE_LOG_PATTERN:-"
+ "%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- [%t] "
+ "%-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"));
config.logger("org.apache.catalina.startup.DigesterFactory", Level.ERROR); config.logger("org.apache.catalina.startup.DigesterFactory", Level.ERROR);
config.logger("org.apache.catalina.util.LifecycleBase", Level.ERROR); config.logger("org.apache.catalina.util.LifecycleBase", Level.ERROR);
config.logger("org.apache.coyote.http11.Http11NioProtocol", Level.WARN); config.logger("org.apache.coyote.http11.Http11NioProtocol", Level.WARN);
@ -115,8 +86,7 @@ class DefaultLogbackConfiguration {
private Appender<ILoggingEvent> consoleAppender(LogbackConfigurator config) { private Appender<ILoggingEvent> consoleAppender(LogbackConfigurator config) {
ConsoleAppender<ILoggingEvent> appender = new ConsoleAppender<>(); ConsoleAppender<ILoggingEvent> appender = new ConsoleAppender<>();
PatternLayoutEncoder encoder = new PatternLayoutEncoder(); PatternLayoutEncoder encoder = new PatternLayoutEncoder();
String logPattern = this.patterns.getProperty("logging.pattern.console", CONSOLE_LOG_PATTERN); encoder.setPattern(resolve(config, "${CONSOLE_LOG_PATTERN}"));
encoder.setPattern(OptionHelper.substVars(logPattern, config.getContext()));
config.start(encoder); config.start(encoder);
appender.setEncoder(encoder); appender.setEncoder(encoder);
config.appender("CONSOLE", appender); config.appender("CONSOLE", appender);
@ -126,8 +96,7 @@ class DefaultLogbackConfiguration {
private Appender<ILoggingEvent> fileAppender(LogbackConfigurator config, String logFile) { private Appender<ILoggingEvent> fileAppender(LogbackConfigurator config, String logFile) {
RollingFileAppender<ILoggingEvent> appender = new RollingFileAppender<>(); RollingFileAppender<ILoggingEvent> appender = new RollingFileAppender<>();
PatternLayoutEncoder encoder = new PatternLayoutEncoder(); PatternLayoutEncoder encoder = new PatternLayoutEncoder();
String logPattern = this.patterns.getProperty("logging.pattern.file", FILE_LOG_PATTERN); encoder.setPattern(resolve(config, "${FILE_LOG_PATTERN}"));
encoder.setPattern(OptionHelper.substVars(logPattern, config.getContext()));
appender.setEncoder(encoder); appender.setEncoder(encoder);
config.start(encoder); config.start(encoder);
appender.setFile(logFile); appender.setFile(logFile);
@ -140,45 +109,32 @@ class DefaultLogbackConfiguration {
String logFile) { String logFile) {
SizeAndTimeBasedRollingPolicy<ILoggingEvent> rollingPolicy = new SizeAndTimeBasedRollingPolicy<>(); SizeAndTimeBasedRollingPolicy<ILoggingEvent> rollingPolicy = new SizeAndTimeBasedRollingPolicy<>();
rollingPolicy.setContext(config.getContext()); rollingPolicy.setContext(config.getContext());
rollingPolicy.setCleanHistoryOnStart(
this.patterns.getProperty("logging.file.clean-history-on-start", Boolean.class, false));
rollingPolicy.setFileNamePattern( rollingPolicy.setFileNamePattern(
this.patterns.getProperty("logging.pattern.rolling-file-name", logFile + ".%d{yyyy-MM-dd}.%i.gz")); resolve(config, "${LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN:-${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz}"));
setMaxFileSize(rollingPolicy, getDataSize("logging.file.max-size", MAX_FILE_SIZE)); rollingPolicy.setCleanHistoryOnStart(
rollingPolicy resolveBoolean(config, "${LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START:-false}"));
.setMaxHistory(this.patterns.getProperty("logging.file.max-history", Integer.class, MAX_FILE_HISTORY)); rollingPolicy.setMaxFileSize(resolveFileSize(config, "${LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE:-10MB}"));
DataSize totalSizeCap = getDataSize("logging.file.total-size-cap", rollingPolicy.setTotalSizeCap(resolveFileSize(config, "${LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP:-0}"));
DataSize.ofBytes(CoreConstants.UNBOUNDED_TOTAL_SIZE_CAP)); rollingPolicy.setMaxHistory(resolveInt(config, "${LOGBACK_ROLLINGPOLICY_MAX_HISTORY:-7}"));
rollingPolicy.setTotalSizeCap(new FileSize(totalSizeCap.toBytes()));
appender.setRollingPolicy(rollingPolicy); appender.setRollingPolicy(rollingPolicy);
rollingPolicy.setParent(appender); rollingPolicy.setParent(appender);
config.start(rollingPolicy); config.start(rollingPolicy);
} }
private void setMaxFileSize(SizeAndTimeBasedRollingPolicy<ILoggingEvent> rollingPolicy, DataSize maxFileSize) { private boolean resolveBoolean(LogbackConfigurator config, String val) {
try { return Boolean.parseBoolean(resolve(config, val));
rollingPolicy.setMaxFileSize(new FileSize(maxFileSize.toBytes()));
}
catch (NoSuchMethodError ex) {
// Logback < 1.1.8 used String configuration
Method method = ReflectionUtils.findMethod(SizeAndTimeBasedRollingPolicy.class, "setMaxFileSize",
String.class);
ReflectionUtils.invokeMethod(method, rollingPolicy, String.valueOf(maxFileSize.toBytes()));
}
} }
private DataSize getDataSize(String property, DataSize defaultSize) { private int resolveInt(LogbackConfigurator config, String val) {
String value = this.patterns.getProperty(property); return Integer.parseInt(resolve(config, val));
if (value == null) {
return defaultSize;
}
try {
return DataSize.parse(value);
} }
catch (IllegalArgumentException ex) {
FileSize fileSize = FileSize.valueOf(value); private FileSize resolveFileSize(LogbackConfigurator config, String val) {
return DataSize.ofBytes(fileSize.getSize()); return FileSize.valueOf(resolve(config, val));
} }
private String resolve(LogbackConfigurator config, String val) {
return OptionHelper.substVars(val, config.getContext());
} }
} }

@ -48,10 +48,12 @@ import org.springframework.boot.logging.LoggerConfiguration;
import org.springframework.boot.logging.LoggingInitializationContext; import org.springframework.boot.logging.LoggingInitializationContext;
import org.springframework.boot.logging.LoggingSystem; import org.springframework.boot.logging.LoggingSystem;
import org.springframework.boot.logging.LoggingSystemFactory; import org.springframework.boot.logging.LoggingSystemFactory;
import org.springframework.boot.logging.LoggingSystemProperties;
import org.springframework.boot.logging.Slf4JLoggingSystem; import org.springframework.boot.logging.Slf4JLoggingSystem;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.SpringProperties; import org.springframework.core.SpringProperties;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
import org.springframework.util.ResourceUtils; import org.springframework.util.ResourceUtils;
@ -100,6 +102,11 @@ public class LogbackLoggingSystem extends Slf4JLoggingSystem {
super(classLoader); super(classLoader);
} }
@Override
public LoggingSystemProperties getSystemProperties(ConfigurableEnvironment environment) {
return new LogbackLoggingSystemProperties(environment);
}
@Override @Override
protected String[] getStandardConfigLocations() { protected String[] getStandardConfigLocations() {
return new String[] { "logback-test.groovy", "logback-test.xml", "logback.groovy", "logback.xml" }; return new String[] { "logback-test.groovy", "logback-test.xml", "logback.groovy", "logback.xml" };

@ -0,0 +1,113 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.logging.logback;
import ch.qos.logback.core.util.FileSize;
import org.springframework.boot.logging.LogFile;
import org.springframework.boot.logging.LoggingSystemProperties;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.core.convert.ConverterNotFoundException;
import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertyResolver;
import org.springframework.util.unit.DataSize;
/**
* {@link LoggingSystemProperties} for Logback.
*
* @author Phillip Webb
* @since 2.4.0
*/
public class LogbackLoggingSystemProperties extends LoggingSystemProperties {
/**
* The name of the System property that contains the rolled-over log file name
* pattern.
*/
public static final String ROLLINGPOLICY_FILE_NAME_PATTERN = "LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN";
/**
* The name of the System property that contains the clean history on start flag.
*/
public static final String ROLLINGPOLICY_CLEAN_HISTORY_ON_START = "LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START";
/**
* The name of the System property that contains the file log max size.
*/
public static final String ROLLINGPOLICY_MAX_FILE_SIZE = "LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE";
/**
* The name of the System property that contains the file total size cap.
*/
public static final String ROLLINGPOLICY_TOTAL_SIZE_CAP = "LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP";
/**
* The name of the System property that contains the file log max history.
*/
public static final String ROLLINGPOLICY_MAX_HISTORY = "LOGBACK_ROLLINGPOLICY_MAX_HISTORY";
public LogbackLoggingSystemProperties(Environment environment) {
super(environment);
}
@Override
protected void apply(LogFile logFile, PropertyResolver resolver) {
super.apply(logFile, resolver);
applyRollingPolicy(resolver, ROLLINGPOLICY_FILE_NAME_PATTERN, "logging.logback.rollingpolicy.file-name-pattern",
"logging.pattern.rolling-file-name");
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");
}
private void applyRollingPolicy(PropertyResolver resolver, String systemPropertyName, String propertyName,
String deprecatedPropertyName) {
applyRollingPolicy(resolver, systemPropertyName, propertyName, deprecatedPropertyName, String.class);
}
private <T> void applyRollingPolicy(PropertyResolver resolver, String systemPropertyName, String propertyName,
String deprecatedPropertyName, Class<T> type) {
T value = getProperty(resolver, propertyName, type);
if (value == null) {
value = getProperty(resolver, deprecatedPropertyName, type);
}
if (value != null) {
String stringValue = String.valueOf((value instanceof DataSize) ? ((DataSize) value).toBytes() : value);
setSystemProperty(systemPropertyName, stringValue);
}
}
@SuppressWarnings("unchecked")
private <T> T getProperty(PropertyResolver resolver, String key, Class<T> type) {
try {
return resolver.getProperty(key, type);
}
catch (ConversionFailedException | ConverterNotFoundException ex) {
if (type != DataSize.class) {
throw ex;
}
String value = resolver.getProperty(key);
return (T) DataSize.ofBytes(FileSize.valueOf(value).getSize());
}
}
}

@ -35,27 +35,6 @@
"level": "error" "level": "error"
} }
}, },
{
"name": "logging.file.clean-history-on-start",
"type": "java.lang.Boolean",
"description": "Whether to clean the archive log files on startup. Only supported with the default logback setup.",
"sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener",
"defaultValue": false
},
{
"name": "logging.file.max-history",
"type": "java.lang.Integer",
"description": "Maximum number of days archive log files are kept. Only supported with the default logback setup.",
"sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener",
"defaultValue": 7
},
{
"name": "logging.file.max-size",
"type": "org.springframework.util.unit.DataSize",
"description": "Maximum log file size. Only supported with the default logback setup.",
"sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener",
"defaultValue": "10MB"
},
{ {
"name": "logging.file.name", "name": "logging.file.name",
"type": "java.lang.String", "type": "java.lang.String",
@ -68,13 +47,6 @@
"description": "Location of the log file. For instance, `/var/log`.", "description": "Location of the log file. For instance, `/var/log`.",
"sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener" "sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener"
}, },
{
"name": "logging.file.total-size-cap",
"type": "org.springframework.util.unit.DataSize",
"description": "Total size of log backups to be kept. Only supported with the default logback setup.",
"sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener",
"defaultValue": "0B"
},
{ {
"name": "logging.group", "name": "logging.group",
"type": "java.util.Map<java.lang.String,java.util.List<java.lang.String>>", "type": "java.util.Map<java.lang.String,java.util.List<java.lang.String>>",
@ -124,20 +96,103 @@
"sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener", "sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener",
"defaultValue": "%5p" "defaultValue": "%5p"
}, },
{
"name": "logging.register-shutdown-hook",
"type": "java.lang.Boolean",
"description": "Register a shutdown hook for the logging system when it is initialized.",
"sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener",
"defaultValue": false
},
{ {
"name": "logging.pattern.rolling-file-name", "name": "logging.pattern.rolling-file-name",
"type": "java.lang.String", "type": "java.lang.String",
"description": "Pattern for rolled-over log file names. Supported only with the default Logback setup.", "description": "Pattern for rolled-over log file names. Supported only with the default Logback setup.",
"sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener", "sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener",
"defaultValue": "${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz",
"deprecation": {
"replacement": "logging.logback.rollingpolicy.file-name-pattern",
"level": "error"
}
},
{
"name": "logging.file.clean-history-on-start",
"type": "java.lang.Boolean",
"description": "Whether to clean the archive log files on startup. Only supported with the default logback setup.",
"sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener",
"defaultValue": false,
"deprecation": {
"replacement": "logging.logback.rollingpolicy.clean-history-on-start",
"level": "error"
}
},
{
"name": "logging.file.max-size",
"type": "org.springframework.util.unit.DataSize",
"description": "Maximum log file size. Only supported with the default logback setup.",
"sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener",
"defaultValue": "10MB",
"deprecation": {
"replacement": "logging.logback.rollingpolicy.max-file-size",
"level": "error"
}
},
{
"name": "logging.file.total-size-cap",
"type": "org.springframework.util.unit.DataSize",
"description": "Total size of log backups to be kept. Only supported with the default logback setup.",
"sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener",
"defaultValue": "0B",
"deprecation": {
"replacement": "logging.logback.rollingpolicy.total-size-cap",
"level": "error"
}
},
{
"name": "logging.file.max-history",
"type": "java.lang.Integer",
"description": "Maximum number of days archive log files are kept. Only supported with the default logback setup.",
"sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener",
"defaultValue": 7,
"deprecation": {
"replacement": "logging.logback.rollingpolicy.max-history",
"level": "error"
}
},
{
"name": "logging.logback.rollingpolicy.file-name-pattern",
"type": "java.lang.String",
"description": "Pattern for rolled-over log file names.",
"sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener",
"defaultValue": "${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz" "defaultValue": "${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz"
}, },
{ {
"name": "logging.register-shutdown-hook", "name": "logging.logback.rollingpolicy.clean-history-on-start",
"type": "java.lang.Boolean", "type": "java.lang.Boolean",
"description": "Register a shutdown hook for the logging system when it is initialized.", "description": "Whether to clean the archive log files on startup.",
"sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener", "sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener",
"defaultValue": false "defaultValue": false
}, },
{
"name": "logging.logback.rollingpolicy.max-file-size",
"type": "org.springframework.util.unit.DataSize",
"description": "Maximum log file size.",
"sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener",
"defaultValue": "10MB"
},
{
"name": "logging.logback.rollingpolicy.total-size-cap",
"type": "org.springframework.util.unit.DataSize",
"description": "Total size of log backups to be kept.",
"sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener",
"defaultValue": "0B"
},
{
"name": "logging.logback.rollingpolicy.max-history",
"type": "java.lang.Integer",
"description": "Maximum number of days archive log files are kept.",
"sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener",
"defaultValue": 7
},
{ {
"name": "spring.application.index", "name": "spring.application.index",
"type": "java.lang.Integer", "type": "java.lang.Integer",

@ -8,6 +8,7 @@ Default logback configuration provided for import
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" /> <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
<conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" /> <conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
<conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" /> <conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
<property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/> <property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<property name="FILE_LOG_PATTERN" value="${FILE_LOG_PATTERN:-%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- [%t] %-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/> <property name="FILE_LOG_PATTERN" value="${FILE_LOG_PATTERN:-%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- [%t] %-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>

@ -13,11 +13,11 @@ initialization performed by Boot
</encoder> </encoder>
<file>${LOG_FILE}</file> <file>${LOG_FILE}</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<cleanHistoryOnStart>${LOG_FILE_CLEAN_HISTORY_ON_START:-false}</cleanHistoryOnStart> <fileNamePattern>${LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN:-${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz}</fileNamePattern>
<fileNamePattern>${ROLLING_FILE_NAME_PATTERN:-${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz}</fileNamePattern> <cleanHistoryOnStart>${LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START:-false}</cleanHistoryOnStart>
<maxFileSize>${LOG_FILE_MAX_SIZE:-10MB}</maxFileSize> <maxFileSize>${LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE:-10MB}</maxFileSize>
<maxHistory>${LOG_FILE_MAX_HISTORY:-7}</maxHistory> <totalSizeCap>${LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP:-0}</totalSizeCap>
<totalSizeCap>${LOG_FILE_TOTAL_SIZE_CAP:-0}</totalSizeCap> <maxHistory>${LOGBACK_ROLLINGPOLICY_MAX_HISTORY:-7}</maxHistory>
</rollingPolicy> </rollingPolicy>
</appender> </appender>
</included> </included>

@ -21,8 +21,10 @@ import java.io.IOException;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.logging.Handler; import java.util.logging.Handler;
@ -109,10 +111,13 @@ class LoggingApplicationListenerTests {
private File logFile; private File logFile;
private Set<Object> systemPropertyNames;
private CapturedOutput output; private CapturedOutput output;
@BeforeEach @BeforeEach
void init(CapturedOutput output) throws SecurityException, IOException { void init(CapturedOutput output) throws SecurityException, IOException {
this.systemPropertyNames = new HashSet<>(System.getProperties().keySet());
this.output = output; this.output = output;
this.logFile = new File(this.tempDir.toFile(), "foo.log"); this.logFile = new File(this.tempDir.toFile(), "foo.log");
LogManager.getLogManager().readConfiguration(JavaLoggingSystem.class.getResourceAsStream("logging.properties")); LogManager.getLogManager().readConfiguration(JavaLoggingSystem.class.getResourceAsStream("logging.properties"));
@ -131,15 +136,8 @@ class LoggingApplicationListenerTests {
loggingSystem.getShutdownHandler().run(); loggingSystem.getShutdownHandler().run();
} }
System.clearProperty(LoggingSystem.class.getName()); System.clearProperty(LoggingSystem.class.getName());
System.clearProperty(LoggingSystemProperties.LOG_FILE);
System.clearProperty(LoggingSystemProperties.LOG_PATH);
System.clearProperty(LoggingSystemProperties.PID_KEY);
System.clearProperty(LoggingSystemProperties.EXCEPTION_CONVERSION_WORD);
System.clearProperty(LoggingSystemProperties.CONSOLE_LOG_PATTERN);
System.clearProperty(LoggingSystemProperties.FILE_LOG_PATTERN);
System.clearProperty(LoggingSystemProperties.LOG_LEVEL_PATTERN);
System.clearProperty(LoggingSystemProperties.ROLLING_FILE_NAME_PATTERN);
System.clearProperty(LoggingSystem.SYSTEM_PROPERTY); System.clearProperty(LoggingSystem.SYSTEM_PROPERTY);
System.getProperties().keySet().retainAll(this.systemPropertyNames);
if (this.context != null) { if (this.context != null) {
this.context.close(); this.context.close();
} }
@ -467,9 +465,14 @@ class LoggingApplicationListenerTests {
assertThat(System.getProperty(LoggingSystemProperties.LOG_FILE)).isEqualTo(this.logFile.getAbsolutePath()); assertThat(System.getProperty(LoggingSystemProperties.LOG_FILE)).isEqualTo(this.logFile.getAbsolutePath());
assertThat(System.getProperty(LoggingSystemProperties.LOG_LEVEL_PATTERN)).isEqualTo("level"); assertThat(System.getProperty(LoggingSystemProperties.LOG_LEVEL_PATTERN)).isEqualTo("level");
assertThat(System.getProperty(LoggingSystemProperties.LOG_PATH)).isEqualTo("path"); assertThat(System.getProperty(LoggingSystemProperties.LOG_PATH)).isEqualTo("path");
assertThat(System.getProperty(LoggingSystemProperties.PID_KEY)).isNotNull();
assertDeprecated();
}
@SuppressWarnings("deprecation")
private void assertDeprecated() {
assertThat(System.getProperty(LoggingSystemProperties.ROLLING_FILE_NAME_PATTERN)) assertThat(System.getProperty(LoggingSystemProperties.ROLLING_FILE_NAME_PATTERN))
.isEqualTo("my.log.%d{yyyyMMdd}.%i.gz"); .isEqualTo("my.log.%d{yyyyMMdd}.%i.gz");
assertThat(System.getProperty(LoggingSystemProperties.PID_KEY)).isNotNull();
} }
@Test @Test

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2019 the original author or authors. * Copyright 2012-2020 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -84,6 +84,7 @@ class LoggingSystemPropertiesTests {
} }
@Test @Test
@SuppressWarnings("deprecation")
void rollingFileNameIsSet() { void rollingFileNameIsSet() {
new LoggingSystemProperties( new LoggingSystemProperties(
new MockEnvironment().withProperty("logging.pattern.rolling-file-name", "rolling file pattern")) new MockEnvironment().withProperty("logging.pattern.rolling-file-name", "rolling file pattern"))

@ -0,0 +1,97 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.logging.logback;
import java.util.HashSet;
import java.util.Set;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.convert.ApplicationConversionService;
import org.springframework.boot.logging.LoggingSystemProperties;
import org.springframework.core.convert.support.ConfigurableConversionService;
import org.springframework.mock.env.MockEnvironment;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link LogbackLoggingSystemProperties}.
*
* @author Phillip Webb
*/
class LogbackLoggingSystemPropertiesTests {
private Set<Object> systemPropertyNames;
private MockEnvironment environment;
@BeforeEach
void captureSystemPropertyNames() {
this.systemPropertyNames = new HashSet<>(System.getProperties().keySet());
this.environment = new MockEnvironment();
this.environment
.setConversionService((ConfigurableConversionService) ApplicationConversionService.getSharedInstance());
}
@AfterEach
void restoreSystemProperties() {
System.getProperties().keySet().retainAll(this.systemPropertyNames);
}
@Test
void applySetsStandardSystemProperties() {
this.environment.setProperty("logging.pattern.console", "boot");
new LogbackLoggingSystemProperties(this.environment).apply();
assertThat(System.getProperties()).containsEntry(LoggingSystemProperties.CONSOLE_LOG_PATTERN, "boot");
}
@Test
void applySetsLogbackSystemProperties() {
this.environment.setProperty("logging.logback.rollingpolicy.file-name-pattern", "fnp");
this.environment.setProperty("logging.logback.rollingpolicy.clean-history-on-start", "chos");
this.environment.setProperty("logging.logback.rollingpolicy.max-file-size", "1KB");
this.environment.setProperty("logging.logback.rollingpolicy.total-size-cap", "2KB");
this.environment.setProperty("logging.logback.rollingpolicy.max-history", "mh");
new LogbackLoggingSystemProperties(this.environment).apply();
assertThat(System.getProperties())
.containsEntry(LogbackLoggingSystemProperties.ROLLINGPOLICY_FILE_NAME_PATTERN, "fnp")
.containsEntry(LogbackLoggingSystemProperties.ROLLINGPOLICY_CLEAN_HISTORY_ON_START, "chos")
.containsEntry(LogbackLoggingSystemProperties.ROLLINGPOLICY_MAX_FILE_SIZE, "1024")
.containsEntry(LogbackLoggingSystemProperties.ROLLINGPOLICY_TOTAL_SIZE_CAP, "2048")
.containsEntry(LogbackLoggingSystemProperties.ROLLINGPOLICY_MAX_HISTORY, "mh");
}
@Test
void applySetsLogbackSystemPropertiesFromDeprecated() {
this.environment.setProperty("logging.pattern.rolling-file-name", "fnp");
this.environment.setProperty("logging.file.clean-history-on-start", "chos");
this.environment.setProperty("logging.file.max-size", "1KB");
this.environment.setProperty("logging.file.total-size-cap", "2KB");
this.environment.setProperty("logging.file.max-history", "mh");
new LogbackLoggingSystemProperties(this.environment).apply();
assertThat(System.getProperties())
.containsEntry(LogbackLoggingSystemProperties.ROLLINGPOLICY_FILE_NAME_PATTERN, "fnp")
.containsEntry(LogbackLoggingSystemProperties.ROLLINGPOLICY_CLEAN_HISTORY_ON_START, "chos")
.containsEntry(LogbackLoggingSystemProperties.ROLLINGPOLICY_MAX_FILE_SIZE, "1024")
.containsEntry(LogbackLoggingSystemProperties.ROLLINGPOLICY_TOTAL_SIZE_CAP, "2048")
.containsEntry(LogbackLoggingSystemProperties.ROLLINGPOLICY_MAX_HISTORY, "mh");
}
}

@ -40,6 +40,7 @@ import org.slf4j.ILoggerFactory;
import org.slf4j.bridge.SLF4JBridgeHandler; import org.slf4j.bridge.SLF4JBridgeHandler;
import org.slf4j.impl.StaticLoggerBinder; import org.slf4j.impl.StaticLoggerBinder;
import org.springframework.boot.convert.ApplicationConversionService;
import org.springframework.boot.logging.AbstractLoggingSystemTests; import org.springframework.boot.logging.AbstractLoggingSystemTests;
import org.springframework.boot.logging.LogFile; import org.springframework.boot.logging.LogFile;
import org.springframework.boot.logging.LogLevel; import org.springframework.boot.logging.LogLevel;
@ -49,6 +50,9 @@ import org.springframework.boot.logging.LoggingSystem;
import org.springframework.boot.logging.LoggingSystemProperties; import org.springframework.boot.logging.LoggingSystemProperties;
import org.springframework.boot.testsupport.system.CapturedOutput; import org.springframework.boot.testsupport.system.CapturedOutput;
import org.springframework.boot.testsupport.system.OutputCaptureExtension; import org.springframework.boot.testsupport.system.OutputCaptureExtension;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.ConfigurableConversionService;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.mock.env.MockEnvironment; import org.springframework.mock.env.MockEnvironment;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@ -79,6 +83,8 @@ class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
private Logger logger; private Logger logger;
private MockEnvironment environment;
private LoggingInitializationContext initializationContext; private LoggingInitializationContext initializationContext;
private Set<Object> systemPropertyNames; private Set<Object> systemPropertyNames;
@ -88,8 +94,10 @@ class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
this.systemPropertyNames = new HashSet<>(System.getProperties().keySet()); this.systemPropertyNames = new HashSet<>(System.getProperties().keySet());
this.loggingSystem.cleanUp(); this.loggingSystem.cleanUp();
this.logger = ((LoggerContext) StaticLoggerBinder.getSingleton().getLoggerFactory()).getLogger(getClass()); this.logger = ((LoggerContext) StaticLoggerBinder.getSingleton().getLoggerFactory()).getLogger(getClass());
MockEnvironment environment = new MockEnvironment(); this.environment = new MockEnvironment();
this.initializationContext = new LoggingInitializationContext(environment); ConversionService conversionService = ApplicationConversionService.getSharedInstance();
this.environment.setConversionService((ConfigurableConversionService) conversionService);
this.initializationContext = new LoggingInitializationContext(this.environment);
} }
@AfterEach @AfterEach
@ -103,7 +111,7 @@ class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
void noFile(CapturedOutput output) { void noFile(CapturedOutput output) {
this.loggingSystem.beforeInitialize(); this.loggingSystem.beforeInitialize();
this.logger.info("Hidden"); this.logger.info("Hidden");
this.loggingSystem.initialize(this.initializationContext, null, null); initialize(this.initializationContext, null, null);
this.logger.info("Hello world"); this.logger.info("Hello world");
assertThat(output).contains("Hello world").doesNotContain("Hidden"); assertThat(output).contains("Hello world").doesNotContain("Hidden");
assertThat(getLineWithText(output, "Hello world")).contains("INFO"); assertThat(getLineWithText(output, "Hello world")).contains("INFO");
@ -114,7 +122,7 @@ class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
void withFile(CapturedOutput output) { void withFile(CapturedOutput output) {
this.loggingSystem.beforeInitialize(); this.loggingSystem.beforeInitialize();
this.logger.info("Hidden"); this.logger.info("Hidden");
this.loggingSystem.initialize(this.initializationContext, null, getLogFile(null, tmpDir())); initialize(this.initializationContext, null, getLogFile(null, tmpDir()));
this.logger.info("Hello world"); this.logger.info("Hello world");
File file = new File(tmpDir() + "/spring.log"); File file = new File(tmpDir() + "/spring.log");
assertThat(output).doesNotContain("LOGBACK:"); assertThat(output).doesNotContain("LOGBACK:");
@ -129,14 +137,14 @@ class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
@Test @Test
void defaultConfigConfiguresAConsoleAppender() { void defaultConfigConfiguresAConsoleAppender() {
this.loggingSystem.beforeInitialize(); this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, null); initialize(this.initializationContext, null, null);
assertThat(getConsoleAppender()).isNotNull(); assertThat(getConsoleAppender()).isNotNull();
} }
@Test @Test
void testNonDefaultConfigLocation(CapturedOutput output) { void testNonDefaultConfigLocation(CapturedOutput output) {
this.loggingSystem.beforeInitialize(); this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, "classpath:logback-nondefault.xml", initialize(this.initializationContext, "classpath:logback-nondefault.xml",
getLogFile(tmpDir() + "/tmp.log", null)); getLogFile(tmpDir() + "/tmp.log", null));
this.logger.info("Hello world"); this.logger.info("Hello world");
assertThat(output).doesNotContain("DEBUG").contains("Hello world").contains(tmpDir() + "/tmp.log") assertThat(output).doesNotContain("DEBUG").contains("Hello world").contains(tmpDir() + "/tmp.log")
@ -149,7 +157,7 @@ class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
System.setProperty("logback.configurationFile", "/foo/my-file.xml"); System.setProperty("logback.configurationFile", "/foo/my-file.xml");
try { try {
this.loggingSystem.beforeInitialize(); this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, null); initialize(this.initializationContext, null, null);
assertThat(output).contains( assertThat(output).contains(
"Ignoring 'logback.configurationFile' system property. Please use 'logging.config' instead."); "Ignoring 'logback.configurationFile' system property. Please use 'logging.config' instead.");
} }
@ -161,8 +169,8 @@ class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
@Test @Test
void testNonexistentConfigLocation() { void testNonexistentConfigLocation() {
this.loggingSystem.beforeInitialize(); this.loggingSystem.beforeInitialize();
assertThatIllegalStateException().isThrownBy(() -> this.loggingSystem.initialize(this.initializationContext, assertThatIllegalStateException()
"classpath:logback-nonexistent.xml", null)); .isThrownBy(() -> initialize(this.initializationContext, "classpath:logback-nonexistent.xml", null));
} }
@Test @Test
@ -174,7 +182,7 @@ class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
@Test @Test
void setLevel(CapturedOutput output) { void setLevel(CapturedOutput output) {
this.loggingSystem.beforeInitialize(); this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, null); initialize(this.initializationContext, null, null);
this.logger.debug("Hello"); this.logger.debug("Hello");
this.loggingSystem.setLogLevel("org.springframework.boot", LogLevel.DEBUG); this.loggingSystem.setLogLevel("org.springframework.boot", LogLevel.DEBUG);
this.logger.debug("Hello"); this.logger.debug("Hello");
@ -184,7 +192,7 @@ class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
@Test @Test
void setLevelToNull(CapturedOutput output) { void setLevelToNull(CapturedOutput output) {
this.loggingSystem.beforeInitialize(); this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, null); initialize(this.initializationContext, null, null);
this.logger.debug("Hello"); this.logger.debug("Hello");
this.loggingSystem.setLogLevel("org.springframework.boot", LogLevel.DEBUG); this.loggingSystem.setLogLevel("org.springframework.boot", LogLevel.DEBUG);
this.logger.debug("Hello"); this.logger.debug("Hello");
@ -196,7 +204,7 @@ class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
@Test @Test
void getLoggingConfigurations() { void getLoggingConfigurations() {
this.loggingSystem.beforeInitialize(); this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, null); initialize(this.initializationContext, null, null);
this.loggingSystem.setLogLevel(getClass().getName(), LogLevel.DEBUG); this.loggingSystem.setLogLevel(getClass().getName(), LogLevel.DEBUG);
List<LoggerConfiguration> configurations = this.loggingSystem.getLoggerConfigurations(); List<LoggerConfiguration> configurations = this.loggingSystem.getLoggerConfigurations();
assertThat(configurations).isNotEmpty(); assertThat(configurations).isNotEmpty();
@ -206,7 +214,7 @@ class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
@Test @Test
void getLoggingConfiguration() { void getLoggingConfiguration() {
this.loggingSystem.beforeInitialize(); this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, null); initialize(this.initializationContext, null, null);
this.loggingSystem.setLogLevel(getClass().getName(), LogLevel.DEBUG); this.loggingSystem.setLogLevel(getClass().getName(), LogLevel.DEBUG);
LoggerConfiguration configuration = this.loggingSystem.getLoggerConfiguration(getClass().getName()); LoggerConfiguration configuration = this.loggingSystem.getLoggerConfiguration(getClass().getName());
assertThat(configuration) assertThat(configuration)
@ -216,7 +224,7 @@ class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
@Test @Test
void getLoggingConfigurationForLoggerThatDoesNotExistShouldReturnNull() { void getLoggingConfigurationForLoggerThatDoesNotExistShouldReturnNull() {
this.loggingSystem.beforeInitialize(); this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, null); initialize(this.initializationContext, null, null);
LoggerConfiguration configuration = this.loggingSystem.getLoggerConfiguration("doesnotexist"); LoggerConfiguration configuration = this.loggingSystem.getLoggerConfiguration("doesnotexist");
assertThat(configuration).isNull(); assertThat(configuration).isNull();
} }
@ -224,7 +232,7 @@ class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
@Test @Test
void getLoggingConfigurationForALL() { void getLoggingConfigurationForALL() {
this.loggingSystem.beforeInitialize(); this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, null); initialize(this.initializationContext, null, null);
Logger logger = (Logger) StaticLoggerBinder.getSingleton().getLoggerFactory().getLogger(getClass().getName()); Logger logger = (Logger) StaticLoggerBinder.getSingleton().getLoggerFactory().getLogger(getClass().getName());
logger.setLevel(Level.ALL); logger.setLevel(Level.ALL);
LoggerConfiguration configuration = this.loggingSystem.getLoggerConfiguration(getClass().getName()); LoggerConfiguration configuration = this.loggingSystem.getLoggerConfiguration(getClass().getName());
@ -235,7 +243,7 @@ class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
@Test @Test
void systemLevelTraceShouldReturnNativeLevelTraceNotAll() { void systemLevelTraceShouldReturnNativeLevelTraceNotAll() {
this.loggingSystem.beforeInitialize(); this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, null); initialize(this.initializationContext, null, null);
this.loggingSystem.setLogLevel(getClass().getName(), LogLevel.TRACE); this.loggingSystem.setLogLevel(getClass().getName(), LogLevel.TRACE);
Logger logger = (Logger) StaticLoggerBinder.getSingleton().getLoggerFactory().getLogger(getClass().getName()); Logger logger = (Logger) StaticLoggerBinder.getSingleton().getLoggerFactory().getLogger(getClass().getName());
assertThat(logger.getLevel()).isEqualTo(Level.TRACE); assertThat(logger.getLevel()).isEqualTo(Level.TRACE);
@ -244,7 +252,7 @@ class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
@Test @Test
void loggingThatUsesJulIsCaptured(CapturedOutput output) { void loggingThatUsesJulIsCaptured(CapturedOutput output) {
this.loggingSystem.beforeInitialize(); this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, null); initialize(this.initializationContext, null, null);
java.util.logging.Logger julLogger = java.util.logging.Logger.getLogger(getClass().getName()); java.util.logging.Logger julLogger = java.util.logging.Logger.getLogger(getClass().getName());
julLogger.info("Hello world"); julLogger.info("Hello world");
assertThat(output).contains("Hello world"); assertThat(output).contains("Hello world");
@ -253,7 +261,7 @@ class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
@Test @Test
void loggingLevelIsPropagatedToJul(CapturedOutput output) { void loggingLevelIsPropagatedToJul(CapturedOutput output) {
this.loggingSystem.beforeInitialize(); this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, null); initialize(this.initializationContext, null, null);
this.loggingSystem.setLogLevel(getClass().getName(), LogLevel.DEBUG); this.loggingSystem.setLogLevel(getClass().getName(), LogLevel.DEBUG);
java.util.logging.Logger julLogger = java.util.logging.Logger.getLogger(getClass().getName()); java.util.logging.Logger julLogger = java.util.logging.Logger.getLogger(getClass().getName());
julLogger.fine("Hello debug world"); julLogger.fine("Hello debug world");
@ -296,33 +304,30 @@ class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
@Test @Test
void testConsolePatternProperty(CapturedOutput output) { void testConsolePatternProperty(CapturedOutput output) {
MockEnvironment environment = new MockEnvironment(); this.environment.setProperty("logging.pattern.console", "%logger %msg");
environment.setProperty("logging.pattern.console", "%logger %msg"); LoggingInitializationContext loggingInitializationContext = new LoggingInitializationContext(this.environment);
LoggingInitializationContext loggingInitializationContext = new LoggingInitializationContext(environment); initialize(loggingInitializationContext, null, null);
this.loggingSystem.initialize(loggingInitializationContext, null, null);
this.logger.info("Hello world"); this.logger.info("Hello world");
assertThat(getLineWithText(output, "Hello world")).doesNotContain("INFO"); assertThat(getLineWithText(output, "Hello world")).doesNotContain("INFO");
} }
@Test @Test
void testLevelPatternProperty(CapturedOutput output) { void testLevelPatternProperty(CapturedOutput output) {
MockEnvironment environment = new MockEnvironment(); this.environment.setProperty("logging.pattern.level", "X%clr(%p)X");
environment.setProperty("logging.pattern.level", "X%clr(%p)X"); new LoggingSystemProperties(this.environment).apply();
new LoggingSystemProperties(environment).apply(); LoggingInitializationContext loggingInitializationContext = new LoggingInitializationContext(this.environment);
LoggingInitializationContext loggingInitializationContext = new LoggingInitializationContext(environment); initialize(loggingInitializationContext, null, null);
this.loggingSystem.initialize(loggingInitializationContext, null, null);
this.logger.info("Hello world"); this.logger.info("Hello world");
assertThat(getLineWithText(output, "Hello world")).contains("XINFOX"); assertThat(getLineWithText(output, "Hello world")).contains("XINFOX");
} }
@Test @Test
void testFilePatternProperty(CapturedOutput output) { void testFilePatternProperty(CapturedOutput output) {
MockEnvironment environment = new MockEnvironment(); this.environment.setProperty("logging.pattern.file", "%logger %msg");
environment.setProperty("logging.pattern.file", "%logger %msg"); LoggingInitializationContext loggingInitializationContext = new LoggingInitializationContext(this.environment);
LoggingInitializationContext loggingInitializationContext = new LoggingInitializationContext(environment);
File file = new File(tmpDir(), "logback-test.log"); File file = new File(tmpDir(), "logback-test.log");
LogFile logFile = getLogFile(file.getPath(), null); LogFile logFile = getLogFile(file.getPath(), null);
this.loggingSystem.initialize(loggingInitializationContext, null, logFile); initialize(loggingInitializationContext, null, logFile);
this.logger.info("Hello world"); this.logger.info("Hello world");
assertThat(getLineWithText(output, "Hello world")).contains("INFO"); assertThat(getLineWithText(output, "Hello world")).contains("INFO");
assertThat(getLineWithText(file, "Hello world")).doesNotContain("INFO"); assertThat(getLineWithText(file, "Hello world")).doesNotContain("INFO");
@ -330,12 +335,11 @@ class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
@Test @Test
void testCleanHistoryOnStartProperty() { void testCleanHistoryOnStartProperty() {
MockEnvironment environment = new MockEnvironment(); this.environment.setProperty("logging.file.clean-history-on-start", "true");
environment.setProperty("logging.file.clean-history-on-start", "true"); LoggingInitializationContext loggingInitializationContext = new LoggingInitializationContext(this.environment);
LoggingInitializationContext loggingInitializationContext = new LoggingInitializationContext(environment);
File file = new File(tmpDir(), "logback-test.log"); File file = new File(tmpDir(), "logback-test.log");
LogFile logFile = getLogFile(file.getPath(), null); LogFile logFile = getLogFile(file.getPath(), null);
this.loggingSystem.initialize(loggingInitializationContext, null, logFile); initialize(loggingInitializationContext, null, logFile);
this.logger.info("Hello world"); this.logger.info("Hello world");
assertThat(getLineWithText(file, "Hello world")).contains("INFO"); assertThat(getLineWithText(file, "Hello world")).contains("INFO");
assertThat(getRollingPolicy().isCleanHistoryOnStart()).isTrue(); assertThat(getRollingPolicy().isCleanHistoryOnStart()).isTrue();
@ -343,12 +347,11 @@ class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
@Test @Test
void testCleanHistoryOnStartPropertyWithXmlConfiguration() { void testCleanHistoryOnStartPropertyWithXmlConfiguration() {
MockEnvironment environment = new MockEnvironment(); this.environment.setProperty("logging.file.clean-history-on-start", "true");
environment.setProperty("logging.file.clean-history-on-start", "true"); LoggingInitializationContext loggingInitializationContext = new LoggingInitializationContext(this.environment);
LoggingInitializationContext loggingInitializationContext = new LoggingInitializationContext(environment);
File file = new File(tmpDir(), "logback-test.log"); File file = new File(tmpDir(), "logback-test.log");
LogFile logFile = getLogFile(file.getPath(), null); LogFile logFile = getLogFile(file.getPath(), null);
this.loggingSystem.initialize(loggingInitializationContext, "classpath:logback-include-base.xml", logFile); initialize(loggingInitializationContext, "classpath:logback-include-base.xml", logFile);
this.logger.info("Hello world"); this.logger.info("Hello world");
assertThat(getLineWithText(file, "Hello world")).contains("INFO"); assertThat(getLineWithText(file, "Hello world")).contains("INFO");
assertThat(getRollingPolicy().isCleanHistoryOnStart()).isTrue(); assertThat(getRollingPolicy().isCleanHistoryOnStart()).isTrue();
@ -370,12 +373,11 @@ class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
} }
private void testMaxFileSizeProperty(String sizeValue, String expectedFileSize) { private void testMaxFileSizeProperty(String sizeValue, String expectedFileSize) {
MockEnvironment environment = new MockEnvironment(); this.environment.setProperty("logging.file.max-size", sizeValue);
environment.setProperty("logging.file.max-size", sizeValue); LoggingInitializationContext loggingInitializationContext = new LoggingInitializationContext(this.environment);
LoggingInitializationContext loggingInitializationContext = new LoggingInitializationContext(environment);
File file = new File(tmpDir(), "logback-test.log"); File file = new File(tmpDir(), "logback-test.log");
LogFile logFile = getLogFile(file.getPath(), null); LogFile logFile = getLogFile(file.getPath(), null);
this.loggingSystem.initialize(loggingInitializationContext, null, logFile); initialize(loggingInitializationContext, null, logFile);
this.logger.info("Hello world"); this.logger.info("Hello world");
assertThat(getLineWithText(file, "Hello world")).contains("INFO"); assertThat(getLineWithText(file, "Hello world")).contains("INFO");
assertThat(ReflectionTestUtils.getField(getRollingPolicy(), "maxFileSize").toString()) assertThat(ReflectionTestUtils.getField(getRollingPolicy(), "maxFileSize").toString())
@ -384,12 +386,11 @@ class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
@Test @Test
void testMaxFileSizePropertyWithXmlConfiguration() { void testMaxFileSizePropertyWithXmlConfiguration() {
MockEnvironment environment = new MockEnvironment(); this.environment.setProperty("logging.file.max-size", "100MB");
environment.setProperty("logging.file.max-size", "100MB"); LoggingInitializationContext loggingInitializationContext = new LoggingInitializationContext(this.environment);
LoggingInitializationContext loggingInitializationContext = new LoggingInitializationContext(environment);
File file = new File(tmpDir(), "logback-test.log"); File file = new File(tmpDir(), "logback-test.log");
LogFile logFile = getLogFile(file.getPath(), null); LogFile logFile = getLogFile(file.getPath(), null);
this.loggingSystem.initialize(loggingInitializationContext, "classpath:logback-include-base.xml", logFile); initialize(loggingInitializationContext, "classpath:logback-include-base.xml", logFile);
this.logger.info("Hello world"); this.logger.info("Hello world");
assertThat(getLineWithText(file, "Hello world")).contains("INFO"); assertThat(getLineWithText(file, "Hello world")).contains("INFO");
assertThat(ReflectionTestUtils.getField(getRollingPolicy(), "maxFileSize").toString()).isEqualTo("100 MB"); assertThat(ReflectionTestUtils.getField(getRollingPolicy(), "maxFileSize").toString()).isEqualTo("100 MB");
@ -397,12 +398,11 @@ class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
@Test @Test
void testMaxHistoryProperty() { void testMaxHistoryProperty() {
MockEnvironment environment = new MockEnvironment(); this.environment.setProperty("logging.file.max-history", "30");
environment.setProperty("logging.file.max-history", "30"); LoggingInitializationContext loggingInitializationContext = new LoggingInitializationContext(this.environment);
LoggingInitializationContext loggingInitializationContext = new LoggingInitializationContext(environment);
File file = new File(tmpDir(), "logback-test.log"); File file = new File(tmpDir(), "logback-test.log");
LogFile logFile = getLogFile(file.getPath(), null); LogFile logFile = getLogFile(file.getPath(), null);
this.loggingSystem.initialize(loggingInitializationContext, null, logFile); initialize(loggingInitializationContext, null, logFile);
this.logger.info("Hello world"); this.logger.info("Hello world");
assertThat(getLineWithText(file, "Hello world")).contains("INFO"); assertThat(getLineWithText(file, "Hello world")).contains("INFO");
assertThat(getRollingPolicy().getMaxHistory()).isEqualTo(30); assertThat(getRollingPolicy().getMaxHistory()).isEqualTo(30);
@ -410,12 +410,11 @@ class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
@Test @Test
void testMaxHistoryPropertyWithXmlConfiguration() { void testMaxHistoryPropertyWithXmlConfiguration() {
MockEnvironment environment = new MockEnvironment(); this.environment.setProperty("logging.file.max-history", "30");
environment.setProperty("logging.file.max-history", "30"); LoggingInitializationContext loggingInitializationContext = new LoggingInitializationContext(this.environment);
LoggingInitializationContext loggingInitializationContext = new LoggingInitializationContext(environment);
File file = new File(tmpDir(), "logback-test.log"); File file = new File(tmpDir(), "logback-test.log");
LogFile logFile = getLogFile(file.getPath(), null); LogFile logFile = getLogFile(file.getPath(), null);
this.loggingSystem.initialize(loggingInitializationContext, "classpath:logback-include-base.xml", logFile); initialize(loggingInitializationContext, "classpath:logback-include-base.xml", logFile);
this.logger.info("Hello world"); this.logger.info("Hello world");
assertThat(getLineWithText(file, "Hello world")).contains("INFO"); assertThat(getLineWithText(file, "Hello world")).contains("INFO");
assertThat(getRollingPolicy().getMaxHistory()).isEqualTo(30); assertThat(getRollingPolicy().getMaxHistory()).isEqualTo(30);
@ -437,12 +436,11 @@ class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
} }
private void testTotalSizeCapProperty(String sizeValue, String expectedFileSize) { private void testTotalSizeCapProperty(String sizeValue, String expectedFileSize) {
MockEnvironment environment = new MockEnvironment(); this.environment.setProperty("logging.file.total-size-cap", sizeValue);
environment.setProperty("logging.file.total-size-cap", sizeValue); LoggingInitializationContext loggingInitializationContext = new LoggingInitializationContext(this.environment);
LoggingInitializationContext loggingInitializationContext = new LoggingInitializationContext(environment);
File file = new File(tmpDir(), "logback-test.log"); File file = new File(tmpDir(), "logback-test.log");
LogFile logFile = getLogFile(file.getPath(), null); LogFile logFile = getLogFile(file.getPath(), null);
this.loggingSystem.initialize(loggingInitializationContext, null, logFile); initialize(loggingInitializationContext, null, logFile);
this.logger.info("Hello world"); this.logger.info("Hello world");
assertThat(getLineWithText(file, "Hello world")).contains("INFO"); assertThat(getLineWithText(file, "Hello world")).contains("INFO");
assertThat(ReflectionTestUtils.getField(getRollingPolicy(), "totalSizeCap").toString()) assertThat(ReflectionTestUtils.getField(getRollingPolicy(), "totalSizeCap").toString())
@ -452,12 +450,11 @@ class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
@Test @Test
void testTotalSizeCapPropertyWithXmlConfiguration() { void testTotalSizeCapPropertyWithXmlConfiguration() {
String expectedSize = "101 MB"; String expectedSize = "101 MB";
MockEnvironment environment = new MockEnvironment(); this.environment.setProperty("logging.file.total-size-cap", expectedSize);
environment.setProperty("logging.file.total-size-cap", expectedSize); LoggingInitializationContext loggingInitializationContext = new LoggingInitializationContext(this.environment);
LoggingInitializationContext loggingInitializationContext = new LoggingInitializationContext(environment);
File file = new File(tmpDir(), "logback-test.log"); File file = new File(tmpDir(), "logback-test.log");
LogFile logFile = getLogFile(file.getPath(), null); LogFile logFile = getLogFile(file.getPath(), null);
this.loggingSystem.initialize(loggingInitializationContext, "classpath:logback-include-base.xml", logFile); initialize(loggingInitializationContext, "classpath:logback-include-base.xml", logFile);
this.logger.info("Hello world"); this.logger.info("Hello world");
assertThat(getLineWithText(file, "Hello world")).contains("INFO"); assertThat(getLineWithText(file, "Hello world")).contains("INFO");
assertThat(ReflectionTestUtils.getField(getRollingPolicy(), "totalSizeCap").toString()).isEqualTo(expectedSize); assertThat(ReflectionTestUtils.getField(getRollingPolicy(), "totalSizeCap").toString()).isEqualTo(expectedSize);
@ -466,7 +463,7 @@ class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
@Test @Test
void exceptionsIncludeClassPackaging(CapturedOutput output) { void exceptionsIncludeClassPackaging(CapturedOutput output) {
this.loggingSystem.beforeInitialize(); this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, getLogFile(null, tmpDir())); initialize(this.initializationContext, null, getLogFile(null, tmpDir()));
this.logger.warn("Expected exception", new RuntimeException("Expected")); this.logger.warn("Expected exception", new RuntimeException("Expected"));
String fileContents = contentOf(new File(tmpDir() + "/spring.log")); String fileContents = contentOf(new File(tmpDir() + "/spring.log"));
assertThat(fileContents).contains("[junit-"); assertThat(fileContents).contains("[junit-");
@ -479,7 +476,7 @@ class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
try { try {
this.loggingSystem.beforeInitialize(); this.loggingSystem.beforeInitialize();
this.logger.info("Hidden"); this.logger.info("Hidden");
this.loggingSystem.initialize(this.initializationContext, null, getLogFile(null, tmpDir())); initialize(this.initializationContext, null, getLogFile(null, tmpDir()));
this.logger.warn("Expected exception", new RuntimeException("Expected", new RuntimeException("Cause"))); this.logger.warn("Expected exception", new RuntimeException("Expected", new RuntimeException("Cause")));
String fileContents = contentOf(new File(tmpDir() + "/spring.log")); String fileContents = contentOf(new File(tmpDir() + "/spring.log"));
assertThat(fileContents).contains("java.lang.RuntimeException: Expected").doesNotContain("Wrapped by:"); assertThat(fileContents).contains("java.lang.RuntimeException: Expected").doesNotContain("Wrapped by:");
@ -496,7 +493,7 @@ class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
this.loggingSystem.beforeInitialize(); this.loggingSystem.beforeInitialize();
this.logger.info("Hidden"); this.logger.info("Hidden");
LogFile logFile = getLogFile(tmpDir() + "/example.log", null, false); LogFile logFile = getLogFile(tmpDir() + "/example.log", null, false);
this.loggingSystem.initialize(this.initializationContext, "classpath:logback-nondefault.xml", logFile); initialize(this.initializationContext, "classpath:logback-nondefault.xml", logFile);
assertThat(System.getProperty(LoggingSystemProperties.LOG_FILE)).endsWith("example.log"); assertThat(System.getProperty(LoggingSystemProperties.LOG_FILE)).endsWith("example.log");
} }
@ -506,24 +503,23 @@ class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
LoggerContextListener listener = mock(LoggerContextListener.class); LoggerContextListener listener = mock(LoggerContextListener.class);
loggerContext.addListener(listener); loggerContext.addListener(listener);
this.loggingSystem.beforeInitialize(); this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, null); initialize(this.initializationContext, null, null);
this.loggingSystem.beforeInitialize(); this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, null); initialize(this.initializationContext, null, null);
verify(listener, times(1)).onReset(loggerContext); verify(listener, times(1)).onReset(loggerContext);
this.loggingSystem.cleanUp(); this.loggingSystem.cleanUp();
loggerContext.addListener(listener); loggerContext.addListener(listener);
this.loggingSystem.beforeInitialize(); this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, null); initialize(this.initializationContext, null, null);
verify(listener, times(2)).onReset(loggerContext); verify(listener, times(2)).onReset(loggerContext);
} }
@Test @Test
void testDateformatPatternProperty(CapturedOutput output) { void testDateformatPatternProperty(CapturedOutput output) {
MockEnvironment environment = new MockEnvironment(); this.environment.setProperty("logging.pattern.dateformat", "yyyy-MM-dd'T'hh:mm:ss.SSSZ");
environment.setProperty("logging.pattern.dateformat", "yyyy-MM-dd'T'hh:mm:ss.SSSZ"); new LoggingSystemProperties(this.environment).apply();
new LoggingSystemProperties(environment).apply(); LoggingInitializationContext loggingInitializationContext = new LoggingInitializationContext(this.environment);
LoggingInitializationContext loggingInitializationContext = new LoggingInitializationContext(environment); initialize(loggingInitializationContext, null, null);
this.loggingSystem.initialize(loggingInitializationContext, null, null);
this.logger.info("Hello world"); this.logger.info("Hello world");
assertThat(getLineWithText(output, "Hello world")) assertThat(getLineWithText(output, "Hello world"))
.containsPattern("\\d{4}-\\d{2}\\-\\d{2}T\\d{2}:\\d{2}:\\d{2}"); .containsPattern("\\d{4}-\\d{2}\\-\\d{2}T\\d{2}:\\d{2}:\\d{2}");
@ -535,7 +531,7 @@ class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
this.loggingSystem.beforeInitialize(); this.loggingSystem.beforeInitialize();
File file = new File(tmpDir(), "logback-test.log"); File file = new File(tmpDir(), "logback-test.log");
LogFile logFile = getLogFile(file.getPath(), null); LogFile logFile = getLogFile(file.getPath(), null);
this.loggingSystem.initialize(this.initializationContext, null, logFile); initialize(this.initializationContext, null, logFile);
assertThat(output).doesNotContain("LevelChangePropagator").doesNotContain("SizeAndTimeBasedFNATP"); assertThat(output).doesNotContain("LevelChangePropagator").doesNotContain("SizeAndTimeBasedFNATP");
} }
@ -546,7 +542,7 @@ class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
this.loggingSystem.beforeInitialize(); this.loggingSystem.beforeInitialize();
File file = new File(tmpDir(), "logback-test.log"); File file = new File(tmpDir(), "logback-test.log");
LogFile logFile = getLogFile(file.getPath(), null); LogFile logFile = getLogFile(file.getPath(), null);
this.loggingSystem.initialize(this.initializationContext, null, logFile); initialize(this.initializationContext, null, logFile);
assertThat(output).contains("LevelChangePropagator").contains("SizeAndTimeBasedFNATP") assertThat(output).contains("LevelChangePropagator").contains("SizeAndTimeBasedFNATP")
.contains("DebugLogbackConfigurator"); .contains("DebugLogbackConfigurator");
} }
@ -557,18 +553,22 @@ class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
@Test @Test
void testRollingFileNameProperty() { void testRollingFileNameProperty() {
MockEnvironment environment = new MockEnvironment();
String rollingFile = "my.log.%d{yyyyMMdd}.%i.gz"; String rollingFile = "my.log.%d{yyyyMMdd}.%i.gz";
environment.setProperty("logging.pattern.rolling-file-name", rollingFile); this.environment.setProperty("logging.pattern.rolling-file-name", rollingFile);
LoggingInitializationContext loggingInitializationContext = new LoggingInitializationContext(environment); LoggingInitializationContext loggingInitializationContext = new LoggingInitializationContext(this.environment);
File file = new File(tmpDir(), "my.log"); File file = new File(tmpDir(), "my.log");
LogFile logFile = getLogFile(file.getPath(), null); LogFile logFile = getLogFile(file.getPath(), null);
this.loggingSystem.initialize(loggingInitializationContext, null, logFile); initialize(loggingInitializationContext, null, logFile);
this.logger.info("Hello world"); this.logger.info("Hello world");
assertThat(getLineWithText(file, "Hello world")).contains("INFO"); assertThat(getLineWithText(file, "Hello world")).contains("INFO");
assertThat(getRollingPolicy().getFileNamePattern()).isEqualTo(rollingFile); assertThat(getRollingPolicy().getFileNamePattern()).isEqualTo(rollingFile);
} }
private void initialize(LoggingInitializationContext context, String configLocation, LogFile logFile) {
this.loggingSystem.getSystemProperties((ConfigurableEnvironment) context.getEnvironment()).apply(logFile);
this.loggingSystem.initialize(context, configLocation, logFile);
}
private static Logger getRootLogger() { private static Logger getRootLogger() {
ILoggerFactory factory = StaticLoggerBinder.getSingleton().getLoggerFactory(); ILoggerFactory factory = StaticLoggerBinder.getSingleton().getLoggerFactory();
LoggerContext context = (LoggerContext) factory; LoggerContext context = (LoggerContext) factory;

@ -1,4 +1,4 @@
<configuration> <configuration>
<property name="ROLLING_FILE_NAME_PATTERN" value="my.log.%d{yyyyMMdd}.%i.gz"/> <property name="LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN" value="my.log.%d{yyyyMMdd}.%i.gz"/>
<include resource="org/springframework/boot/logging/logback/base.xml" /> <include resource="org/springframework/boot/logging/logback/base.xml" />
</configuration> </configuration>

Loading…
Cancel
Save