Use SizeAndTimeBasedRollingPolicy file appender

Update the logback file appender to use `SizeAndTimeBasedRollingPolicy`
rather than `FixedWindowRollingPolicy`.

Add two new properties to improve log file configuration capabilities:

 - `logging.file.max-history` to limit the number of archive log files
    to keep.
 - `logging.file.max-size` to limit the log file size.

See gh-6352
This commit is contained in:
Vedran Pavic 2016-07-05 11:29:35 +02:00 committed by Phillip Webb
parent f440fc6d25
commit 991468b0ef
7 changed files with 124 additions and 32 deletions

View File

@ -41,6 +41,8 @@ content into your application; rather pick only the properties that you need.
logging.config= # Location of the logging configuration file. For instance `classpath:logback.xml` for Logback
logging.exception-conversion-word=%wEx # Conversion word used when logging exceptions.
logging.file= # Log file name. For instance `myapp.log`
logging.file.max-history= # Maximum of archive log files to keep. Only supported with the default logback setup.
logging.file.max-size= # Maximum log file size. Only supported with the default logback setup.
logging.level.*= # Log levels severity mapping. For instance `logging.level.org.springframework=DEBUG`
logging.path= # Location of the log file. For instance `/var/log`
logging.pattern.console= # Appender pattern for output to the console. Only supported with the default logback setup.

View File

@ -1533,7 +1533,9 @@ relative to the current directory.
|===
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.
`WARN`-level, and `INFO`-level messages are logged by default. Size limits can be changed
using the `logging.file.max-size` property. Previously rotated files are archived
indefinitely unless the `logging.file.max-history` property has been set.
NOTE: The logging system is initialized early in the application lifecycle. Consequently,
logging properties are not found in property files loaded through `@PropertySource`
@ -1620,6 +1622,14 @@ To help with the customization, some other properties are transferred from the S
|`LOG_FILE`
|If defined, it is used in the default log configuration.
|`logging.file.max-size`
|`LOG_FILE_MAX_SIZE`
|Maximum log file size (if LOG_FILE enabled). (Only supported with the default logback setup.)
|`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.)
|`logging.path`
|`LOG_PATH`
|If defined, it is used in the default log configuration.

View File

@ -29,6 +29,7 @@ import org.springframework.util.Assert;
* @author Andy Wilkinson
* @author Phillip Webb
* @author Madhura Bhave
* @author Vedran Pavic
* @since 2.0.0
*/
public class LoggingSystemProperties {
@ -63,6 +64,16 @@ public class LoggingSystemProperties {
*/
public static final String FILE_LOG_PATTERN = "FILE_LOG_PATTERN";
/**
* The name of the System property that contains the file log max history.
*/
public static final String FILE_MAX_HISTORY = "LOG_FILE_MAX_HISTORY";
/**
* The name of the System property that contains the file log max size.
*/
public static final String FILE_MAX_SIZE = "LOG_FILE_MAX_SIZE";
/**
* The name of the System property that contains the log level pattern.
*/
@ -89,6 +100,8 @@ public class LoggingSystemProperties {
"exception-conversion-word");
setSystemProperty(resolver, CONSOLE_LOG_PATTERN, "pattern.console");
setSystemProperty(resolver, FILE_LOG_PATTERN, "pattern.file");
setSystemProperty(resolver, FILE_MAX_HISTORY, "file.max-history");
setSystemProperty(resolver, FILE_MAX_SIZE, "file.max-size");
setSystemProperty(resolver, LOG_LEVEL_PATTERN, "pattern.level");
setSystemProperty(PID_KEY, new ApplicationPid().toString());
if (logFile != null) {

View File

@ -24,9 +24,9 @@ import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.ConsoleAppender;
import ch.qos.logback.core.rolling.FixedWindowRollingPolicy;
import ch.qos.logback.core.CoreConstants;
import ch.qos.logback.core.rolling.RollingFileAppender;
import ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy;
import ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy;
import ch.qos.logback.core.util.FileSize;
import ch.qos.logback.core.util.OptionHelper;
@ -45,6 +45,7 @@ import org.springframework.util.ReflectionUtils;
*
* @author Phillip Webb
* @author Madhura Bhave
* @author Vedran Pavic
* @since 1.1.2
*/
class DefaultLogbackConfiguration {
@ -57,6 +58,8 @@ class DefaultLogbackConfiguration {
private static final String FILE_LOG_PATTERN = "%d{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 String MAX_FILE_SIZE = "10MB";
private static final Charset UTF8 = Charset.forName("UTF-8");
private final PropertyResolver patterns;
@ -140,34 +143,32 @@ class DefaultLogbackConfiguration {
config.start(encoder);
appender.setFile(logFile);
setRollingPolicy(appender, config, logFile);
setMaxFileSize(appender, config);
config.appender("FILE", appender);
return appender;
}
private void setRollingPolicy(RollingFileAppender<ILoggingEvent> appender,
LogbackConfigurator config, String logFile) {
FixedWindowRollingPolicy rollingPolicy = new FixedWindowRollingPolicy();
rollingPolicy.setFileNamePattern(logFile + ".%i");
SizeAndTimeBasedRollingPolicy<ILoggingEvent> rollingPolicy =
new SizeAndTimeBasedRollingPolicy<>();
rollingPolicy.setFileNamePattern(logFile + ".%d{yyyy-MM-dd}.%i.gz");
String maxFileSize = this.patterns.getProperty("logging.file.max-size",
MAX_FILE_SIZE);
try {
rollingPolicy.setMaxFileSize(FileSize.valueOf(maxFileSize));
}
catch (NoSuchMethodError ex) {
// Logback < 1.1.8 used String configuration
Method method = ReflectionUtils.findMethod(
SizeAndTimeBasedRollingPolicy.class, "setMaxFileSize", String.class);
ReflectionUtils.invokeMethod(method, rollingPolicy, maxFileSize);
}
int maxHistory = this.patterns.getProperty("logging.file.max-history",
Integer.class, CoreConstants.UNBOUND_HISTORY);
rollingPolicy.setMaxHistory(maxHistory);
appender.setRollingPolicy(rollingPolicy);
rollingPolicy.setParent(appender);
config.start(rollingPolicy);
}
private void setMaxFileSize(RollingFileAppender<ILoggingEvent> appender,
LogbackConfigurator config) {
SizeBasedTriggeringPolicy<ILoggingEvent> triggeringPolicy = new SizeBasedTriggeringPolicy<>();
try {
triggeringPolicy.setMaxFileSize(FileSize.valueOf("10MB"));
}
catch (NoSuchMethodError ex) {
// Logback < 1.1.8 used String configuration
Method method = ReflectionUtils.findMethod(SizeBasedTriggeringPolicy.class,
"setMaxFileSize", String.class);
ReflectionUtils.invokeMethod(method, triggeringPolicy, "10MB");
}
appender.setTriggeringPolicy(triggeringPolicy);
config.start(triggeringPolicy);
}
}

View File

@ -69,6 +69,20 @@
"description": "Name of the log file. Names can be an exact location or relative to the current directory.",
"sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener"
},
{
"name": "logging.file.max-size",
"type": "java.lang.String",
"description": "Maximum log file size. Only supported with the default logback setup.",
"sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener",
"defaultValue": "10MB"
},
{
"name": "logging.file.max-history",
"type": "java.lang.Integer",
"description": "Maximum number of archive log files to keep. Only supported with the default logback setup.",
"sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener",
"defaultValue": 0
},
{
"name": "logging.level",
"type": "java.util.Map<java.lang.String,java.lang.String>",

View File

@ -12,12 +12,10 @@ initialization performed by Boot
<pattern>${FILE_LOG_PATTERN}</pattern>
</encoder>
<file>${LOG_FILE}</file>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>${LOG_FILE}.%i</fileNamePattern>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz</fileNamePattern>
<maxFileSize>${MAX_FILE_SIZE:-10MB}</maxFileSize>
<maxHistory>${MAX_HISTORY:-0}</maxHistory>
</rollingPolicy>
<triggeringPolicy
class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>10MB</MaxFileSize>
</triggeringPolicy>
</appender>
</included>

View File

@ -27,6 +27,10 @@ import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.spi.LoggerContextListener;
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.SizeAndTimeBasedRollingPolicy;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.impl.SLF4JLogFactory;
import org.hamcrest.Matcher;
@ -48,6 +52,7 @@ import org.springframework.boot.logging.LoggingSystem;
import org.springframework.boot.testsupport.assertj.Matched;
import org.springframework.boot.testsupport.rule.OutputCapture;
import org.springframework.mock.env.MockEnvironment;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.StringUtils;
@ -66,6 +71,7 @@ import static org.mockito.Mockito.verify;
* @author Andy Wilkinson
* @author Ben Hale
* @author Madhura Bhave
* @author Vedran Pavic
*/
public class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
@ -120,15 +126,16 @@ public class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
assertThat(getLineWithText(output, "Hello world")).contains("INFO");
assertThat(file.exists()).isTrue();
assertThat(getLineWithText(file, "Hello world")).contains("INFO");
assertThat(ReflectionTestUtils.getField(getRollingPolicy(), "maxFileSize")
.toString()).isEqualTo("10 MB");
assertThat(getRollingPolicy().getMaxHistory()).isEqualTo(
CoreConstants.UNBOUND_HISTORY);
}
@Test
public void testBasicConfigLocation() throws Exception {
this.loggingSystem.beforeInitialize();
ILoggerFactory factory = StaticLoggerBinder.getSingleton().getLoggerFactory();
LoggerContext context = (LoggerContext) factory;
Logger root = context.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);
assertThat(root.getAppender("CONSOLE")).isNotNull();
assertThat(getConsoleAppender()).isNotNull();
}
@Test
@ -339,6 +346,35 @@ public class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
assertThat(getLineWithText(file, "Hello world")).doesNotContain("INFO");
}
@Test
public void testMaxFileSizeProperty() throws Exception {
MockEnvironment environment = new MockEnvironment();
environment.setProperty("logging.file.max-size", "100MB");
LoggingInitializationContext loggingInitializationContext =
new LoggingInitializationContext(environment);
File file = new File(tmpDir(), "logback-test.log");
LogFile logFile = getLogFile(file.getPath(), null);
this.loggingSystem.initialize(loggingInitializationContext, null, logFile);
this.logger.info("Hello world");
assertThat(getLineWithText(file, "Hello world")).contains("INFO");
assertThat(ReflectionTestUtils.getField(getRollingPolicy(), "maxFileSize")
.toString()).isEqualTo("100 MB");
}
@Test
public void testMaxHistoryProperty() throws Exception {
MockEnvironment environment = new MockEnvironment();
environment.setProperty("logging.file.max-history", "30");
LoggingInitializationContext loggingInitializationContext =
new LoggingInitializationContext(environment);
File file = new File(tmpDir(), "logback-test.log");
LogFile logFile = getLogFile(file.getPath(), null);
this.loggingSystem.initialize(loggingInitializationContext, null, logFile);
this.logger.info("Hello world");
assertThat(getLineWithText(file, "Hello world")).contains("INFO");
assertThat(getRollingPolicy().getMaxHistory()).isEqualTo(30);
}
@Test
public void exceptionsIncludeClassPackaging() throws Exception {
this.loggingSystem.beforeInitialize();
@ -404,6 +440,24 @@ public class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
verify(listener, times(2)).onReset(loggerContext);
}
private static Logger getRootLogger() {
ILoggerFactory factory = StaticLoggerBinder.getSingleton().getLoggerFactory();
LoggerContext context = (LoggerContext) factory;
return context.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);
}
private static ConsoleAppender getConsoleAppender() {
return (ConsoleAppender) getRootLogger().getAppender("CONSOLE");
}
private static RollingFileAppender getFileAppender() {
return (RollingFileAppender) getRootLogger().getAppender("FILE");
}
private static SizeAndTimeBasedRollingPolicy getRollingPolicy() {
return (SizeAndTimeBasedRollingPolicy) getFileAppender().getRollingPolicy();
}
private String getLineWithText(File file, String outputSearch) throws Exception {
return getLineWithText(FileCopyUtils.copyToString(new FileReader(file)),
outputSearch);