Commit 81e6a5a4 authored by Stephane Nicoll's avatar Stephane Nicoll

Merge pull request #15325 from thorntonrp

* pr/15325:
  Polish "Support for capping archived log files"
  Support for capping archived log files
parents 6b799da9 f42cec9e
...@@ -37,10 +37,12 @@ content into your application. Rather, pick only the properties that you need. ...@@ -37,10 +37,12 @@ content into your application. Rather, pick only the properties that you need.
# LOGGING # LOGGING
logging.config= # Location of the logging configuration file. For instance, `classpath:logback.xml` for Logback. 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.exception-conversion-word=%wEx # Conversion word used when logging exceptions.
logging.file.clean-history-on-start=false # Whether to clean the archive log files on startup. Only supported with the default logback setup.
logging.file.max-history=0 # Maximum of archive log files to keep. Only supported with the default logback setup. logging.file.max-history=0 # Maximum of archive log files to keep. Only supported with the default logback setup.
logging.file.max-size=10MB # Maximum log file size. Only supported with the default logback setup. logging.file.max-size=10MB # Maximum log file size. Only supported with the default logback setup.
logging.file.name= # Log file name (for instance, `myapp.log`). Names can be an exact location or relative to the current directory. logging.file.name= # Log file name (for instance, `myapp.log`). Names can be an exact location or relative to the current directory.
logging.file.path= # Location of the log file. For instance, `/var/log`. logging.file.path= # Location of the log file. For instance, `/var/log`.
logging.file.total-size-cap=0 # Total size of log backups to be kept. Only supported with the default logback setup.
logging.group.*= # Log groups to quickly change multiple loggers at the same time. For instance, `logging.level.db=org.hibernate,org.springframework.jdbc`. logging.group.*= # Log groups to quickly change multiple loggers at the same time. For instance, `logging.level.db=org.hibernate,org.springframework.jdbc`.
logging.level.*= # Log levels severity mapping. For instance, `logging.level.org.springframework=DEBUG`. logging.level.*= # Log levels severity mapping. For instance, `logging.level.org.springframework=DEBUG`.
logging.pattern.console= # Appender pattern for output to the console. Supported only with the default Logback setup. logging.pattern.console= # Appender pattern for output to the console. Supported only with the default Logback setup.
......
...@@ -1738,7 +1738,10 @@ relative to the current directory. ...@@ -1738,7 +1738,10 @@ relative to the current directory.
Log files rotate when they reach 10 MB and, as with console output, `ERROR`-level, 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 `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 using the `logging.file.max-size` property. Previously rotated files are archived
indefinitely unless the `logging.file.max-history` property has been set. indefinitely unless the `logging.file.max-history` property has been set. The total size
of log archives can be capped using `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 `logging.file.clean-history-on-start` property.
NOTE: The logging system is initialized early in the application lifecycle. Consequently, NOTE: The logging system is initialized early in the application lifecycle. Consequently,
logging properties are not found in property files loaded through `@PropertySource` logging properties are not found in property files loaded through `@PropertySource`
...@@ -1856,6 +1859,11 @@ To help with the customization, some other properties are transferred from the S ...@@ -1856,6 +1859,11 @@ 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.
|`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.)
|`logging.file.name` |`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.
...@@ -1874,6 +1882,11 @@ the default Logback setup.) ...@@ -1874,6 +1882,11 @@ the default Logback setup.)
|`LOG_PATH` |`LOG_PATH`
|If defined, it is used in the default log configuration. |If defined, it is used in the default log configuration.
|`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.)
|`logging.pattern.console` |`logging.pattern.console`
|`CONSOLE_LOG_PATTERN` |`CONSOLE_LOG_PATTERN`
|The log pattern to use on the console (stdout). (Only supported with the default Logback |The log pattern to use on the console (stdout). (Only supported with the default Logback
......
/* /*
* Copyright 2012-2018 the original author or authors. * Copyright 2012-2019 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.
...@@ -30,6 +30,7 @@ import org.springframework.util.Assert; ...@@ -30,6 +30,7 @@ import org.springframework.util.Assert;
* @author Phillip Webb * @author Phillip Webb
* @author Madhura Bhave * @author Madhura Bhave
* @author Vedran Pavic * @author Vedran Pavic
* @author Robert Thornton
* @since 2.0.0 * @since 2.0.0
*/ */
public class LoggingSystemProperties { public class LoggingSystemProperties {
...@@ -59,6 +60,11 @@ public class LoggingSystemProperties { ...@@ -59,6 +60,11 @@ 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.
*/
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 pattern. * The name of the System property that contains the file log pattern.
*/ */
...@@ -74,6 +80,11 @@ public class LoggingSystemProperties { ...@@ -74,6 +80,11 @@ public class LoggingSystemProperties {
*/ */
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.
*/
public static final String FILE_TOTAL_SIZE_CAP = "LOG_FILE_TOTAL_SIZE_CAP";
/** /**
* The name of the System property that contains the log level pattern. * The name of the System property that contains the log level pattern.
*/ */
...@@ -106,8 +117,11 @@ public class LoggingSystemProperties { ...@@ -106,8 +117,11 @@ public class LoggingSystemProperties {
setSystemProperty(PID_KEY, new ApplicationPid().toString()); setSystemProperty(PID_KEY, new ApplicationPid().toString());
setSystemProperty(resolver, CONSOLE_LOG_PATTERN, "pattern.console"); setSystemProperty(resolver, CONSOLE_LOG_PATTERN, "pattern.console");
setSystemProperty(resolver, FILE_LOG_PATTERN, "pattern.file"); setSystemProperty(resolver, FILE_LOG_PATTERN, "pattern.file");
setSystemProperty(resolver, FILE_CLEAN_HISTORY_ON_START,
"file.clean-history-on-start");
setSystemProperty(resolver, FILE_MAX_HISTORY, "file.max-history"); setSystemProperty(resolver, FILE_MAX_HISTORY, "file.max-history");
setSystemProperty(resolver, FILE_MAX_SIZE, "file.max-size"); setSystemProperty(resolver, FILE_MAX_SIZE, "file.max-size");
setSystemProperty(resolver, FILE_TOTAL_SIZE_CAP, "file.total-size-cap");
setSystemProperty(resolver, LOG_LEVEL_PATTERN, "pattern.level"); setSystemProperty(resolver, LOG_LEVEL_PATTERN, "pattern.level");
setSystemProperty(resolver, LOG_DATEFORMAT_PATTERN, "pattern.dateformat"); setSystemProperty(resolver, LOG_DATEFORMAT_PATTERN, "pattern.dateformat");
if (logFile != null) { if (logFile != null) {
......
/* /*
* Copyright 2012-2018 the original author or authors. * Copyright 2012-2019 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.
...@@ -45,6 +45,7 @@ import org.springframework.util.ReflectionUtils; ...@@ -45,6 +45,7 @@ import org.springframework.util.ReflectionUtils;
* @author Phillip Webb * @author Phillip Webb
* @author Madhura Bhave * @author Madhura Bhave
* @author Vedran Pavic * @author Vedran Pavic
* @author Robert Thornton
* @since 1.1.2 * @since 1.1.2
*/ */
class DefaultLogbackConfiguration { class DefaultLogbackConfiguration {
...@@ -140,11 +141,16 @@ class DefaultLogbackConfiguration { ...@@ -140,11 +141,16 @@ class DefaultLogbackConfiguration {
private void setRollingPolicy(RollingFileAppender<ILoggingEvent> appender, private void setRollingPolicy(RollingFileAppender<ILoggingEvent> appender,
LogbackConfigurator config, String logFile) { LogbackConfigurator config, String logFile) {
SizeAndTimeBasedRollingPolicy<ILoggingEvent> rollingPolicy = new SizeAndTimeBasedRollingPolicy<>(); SizeAndTimeBasedRollingPolicy<ILoggingEvent> rollingPolicy = new SizeAndTimeBasedRollingPolicy<>();
rollingPolicy.setCleanHistoryOnStart(this.patterns.getProperty(
"logging.file.clean-history-on-start", Boolean.class, false));
rollingPolicy.setFileNamePattern(logFile + ".%d{yyyy-MM-dd}.%i.gz"); rollingPolicy.setFileNamePattern(logFile + ".%d{yyyy-MM-dd}.%i.gz");
setMaxFileSize(rollingPolicy, setMaxFileSize(rollingPolicy,
this.patterns.getProperty("logging.file.max-size", MAX_FILE_SIZE)); this.patterns.getProperty("logging.file.max-size", MAX_FILE_SIZE));
rollingPolicy.setMaxHistory(this.patterns.getProperty("logging.file.max-history", rollingPolicy.setMaxHistory(this.patterns.getProperty("logging.file.max-history",
Integer.class, CoreConstants.UNBOUND_HISTORY)); Integer.class, CoreConstants.UNBOUND_HISTORY));
rollingPolicy.setTotalSizeCap(
FileSize.valueOf(this.patterns.getProperty("logging.file.total-size-cap",
String.valueOf(CoreConstants.UNBOUNDED_TOTAL_SIZE_CAP))));
appender.setRollingPolicy(rollingPolicy); appender.setRollingPolicy(rollingPolicy);
rollingPolicy.setParent(appender); rollingPolicy.setParent(appender);
config.start(rollingPolicy); config.start(rollingPolicy);
......
...@@ -76,6 +76,13 @@ ...@@ -76,6 +76,13 @@
"replacement": "logging.file.name" "replacement": "logging.file.name"
} }
}, },
{
"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.name", "name": "logging.file.name",
"type": "java.lang.String", "type": "java.lang.String",
...@@ -102,6 +109,13 @@ ...@@ -102,6 +109,13 @@
"sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener", "sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener",
"defaultValue": 0 "defaultValue": 0
}, },
{
"name": "logging.file.total-size-cap",
"type": "java.lang.String",
"description": "Total size of log backups to be kept. Only supported with the default logback setup.",
"sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener",
"defaultValue": "0"
},
{ {
"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>>",
......
...@@ -13,9 +13,11 @@ initialization performed by Boot ...@@ -13,9 +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>${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz</fileNamePattern> <fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz</fileNamePattern>
<maxFileSize>${LOG_FILE_MAX_SIZE:-10MB}</maxFileSize> <maxFileSize>${LOG_FILE_MAX_SIZE:-10MB}</maxFileSize>
<maxHistory>${LOG_FILE_MAX_HISTORY:-0}</maxHistory> <maxHistory>${LOG_FILE_MAX_HISTORY:-0}</maxHistory>
<totalSizeCap>${LOG_FILE_TOTAL_SIZE_CAP:-0}</totalSizeCap>
</rollingPolicy> </rollingPolicy>
</appender> </appender>
</included> </included>
...@@ -73,6 +73,7 @@ import static org.mockito.Mockito.verify; ...@@ -73,6 +73,7 @@ import static org.mockito.Mockito.verify;
* @author Ben Hale * @author Ben Hale
* @author Madhura Bhave * @author Madhura Bhave
* @author Vedran Pavic * @author Vedran Pavic
* @author Robert Thornton
*/ */
@RunWith(ModifiedClassPathRunner.class) @RunWith(ModifiedClassPathRunner.class)
@ClassPathExclusions("log4j-*.jar") @ClassPathExclusions("log4j-*.jar")
...@@ -351,6 +352,35 @@ public class LogbackLoggingSystemTests extends AbstractLoggingSystemTests { ...@@ -351,6 +352,35 @@ public class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
assertThat(getLineWithText(file, "Hello world")).doesNotContain("INFO"); assertThat(getLineWithText(file, "Hello world")).doesNotContain("INFO");
} }
@Test
public void testCleanHistoryOnStartProperty() {
MockEnvironment environment = new MockEnvironment();
environment.setProperty("logging.file.clean-history-on-start", "true");
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().isCleanHistoryOnStart()).isTrue();
}
@Test
public void testCleanHistoryOnStartPropertyWithXmlConfiguration() {
MockEnvironment environment = new MockEnvironment();
environment.setProperty("logging.file.clean-history-on-start", "true");
LoggingInitializationContext loggingInitializationContext = new LoggingInitializationContext(
environment);
File file = new File(tmpDir(), "logback-test.log");
LogFile logFile = getLogFile(file.getPath(), null);
this.loggingSystem.initialize(loggingInitializationContext,
"classpath:logback-include-base.xml", logFile);
this.logger.info("Hello world");
assertThat(getLineWithText(file, "Hello world")).contains("INFO");
assertThat(getRollingPolicy().isCleanHistoryOnStart()).isTrue();
}
@Test @Test
public void testMaxFileSizeProperty() { public void testMaxFileSizeProperty() {
MockEnvironment environment = new MockEnvironment(); MockEnvironment environment = new MockEnvironment();
...@@ -411,6 +441,39 @@ public class LogbackLoggingSystemTests extends AbstractLoggingSystemTests { ...@@ -411,6 +441,39 @@ public class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
assertThat(getRollingPolicy().getMaxHistory()).isEqualTo(30); assertThat(getRollingPolicy().getMaxHistory()).isEqualTo(30);
} }
@Test
public void testTotalSizeCapProperty() {
String expectedSize = "101 MB";
MockEnvironment environment = new MockEnvironment();
environment.setProperty("logging.file.total-size-cap", expectedSize);
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(), "totalSizeCap")
.toString()).isEqualTo(expectedSize);
}
@Test
public void testTotalSizeCapPropertyWithXmlConfiguration() {
String expectedSize = "101 MB";
MockEnvironment environment = new MockEnvironment();
environment.setProperty("logging.file.total-size-cap", expectedSize);
LoggingInitializationContext loggingInitializationContext = new LoggingInitializationContext(
environment);
File file = new File(tmpDir(), "logback-test.log");
LogFile logFile = getLogFile(file.getPath(), null);
this.loggingSystem.initialize(loggingInitializationContext,
"classpath:logback-include-base.xml", logFile);
this.logger.info("Hello world");
assertThat(getLineWithText(file, "Hello world")).contains("INFO");
assertThat(ReflectionTestUtils.getField(getRollingPolicy(), "totalSizeCap")
.toString()).isEqualTo(expectedSize);
}
@Test @Test
public void exceptionsIncludeClassPackaging() { public void exceptionsIncludeClassPackaging() {
this.loggingSystem.beforeInitialize(); this.loggingSystem.beforeInitialize();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment