Skip to content

Commit 7c5d2fa

Browse files
committed
Merge pull request #6352 from vpavic/gh-5590
* pr/6352: Polish SizeAndTimeBasedRollingPolicy changes Use SizeAndTimeBasedRollingPolicy file appender
2 parents f440fc6 + f0327fb commit 7c5d2fa

File tree

7 files changed

+122
-26
lines changed

7 files changed

+122
-26
lines changed

spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc

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

spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1533,7 +1533,9 @@ relative to the current directory.
15331533
|===
15341534

15351535
Log files rotate when they reach 10 MB and, as with console output, `ERROR`-level,
1536-
`WARN`-level, and `INFO`-level messages are logged by default.
1536+
`WARN`-level, and `INFO`-level messages are logged by default. Size limits can be changed
1537+
using the `logging.file.max-size` property. Previously rotated files are archived
1538+
indefinitely unless the `logging.file.max-history` property has been set.
15371539

15381540
NOTE: The logging system is initialized early in the application lifecycle. Consequently,
15391541
logging properties are not found in property files loaded through `@PropertySource`
@@ -1620,6 +1622,16 @@ To help with the customization, some other properties are transferred from the S
16201622
|`LOG_FILE`
16211623
|If defined, it is used in the default log configuration.
16221624

1625+
|`logging.file.max-size`
1626+
|`LOG_FILE_MAX_SIZE`
1627+
|Maximum log file size (if LOG_FILE enabled). (Only supported with the default logback
1628+
setup.)
1629+
1630+
|`logging.file.max-history`
1631+
|`LOG_FILE_MAX_HISTORY`
1632+
|Maximum number of archive log files to keep (if LOG_FILE enabled). (Only supported with
1633+
the default logback setup.)
1634+
16231635
|`logging.path`
16241636
|`LOG_PATH`
16251637
|If defined, it is used in the default log configuration.
@@ -1645,7 +1657,6 @@ To help with the customization, some other properties are transferred from the S
16451657
environment variable).
16461658
|===
16471659

1648-
16491660
All the supported logging systems can consult System properties when parsing their
16501661
configuration files. See the default configurations in `spring-boot.jar` for examples:
16511662

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/LoggingSystemProperties.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
* @author Andy Wilkinson
3030
* @author Phillip Webb
3131
* @author Madhura Bhave
32+
* @author Vedran Pavic
3233
* @since 2.0.0
3334
*/
3435
public class LoggingSystemProperties {
@@ -63,6 +64,16 @@ public class LoggingSystemProperties {
6364
*/
6465
public static final String FILE_LOG_PATTERN = "FILE_LOG_PATTERN";
6566

67+
/**
68+
* The name of the System property that contains the file log max history.
69+
*/
70+
public static final String FILE_MAX_HISTORY = "LOG_FILE_MAX_HISTORY";
71+
72+
/**
73+
* The name of the System property that contains the file log max size.
74+
*/
75+
public static final String FILE_MAX_SIZE = "LOG_FILE_MAX_SIZE";
76+
6677
/**
6778
* The name of the System property that contains the log level pattern.
6879
*/
@@ -89,6 +100,8 @@ public void apply(LogFile logFile) {
89100
"exception-conversion-word");
90101
setSystemProperty(resolver, CONSOLE_LOG_PATTERN, "pattern.console");
91102
setSystemProperty(resolver, FILE_LOG_PATTERN, "pattern.file");
103+
setSystemProperty(resolver, FILE_MAX_HISTORY, "file.max-history");
104+
setSystemProperty(resolver, FILE_MAX_SIZE, "file.max-size");
92105
setSystemProperty(resolver, LOG_LEVEL_PATTERN, "pattern.level");
93106
setSystemProperty(PID_KEY, new ApplicationPid().toString());
94107
if (logFile != null) {

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/logback/DefaultLogbackConfiguration.java

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@
2424
import ch.qos.logback.classic.spi.ILoggingEvent;
2525
import ch.qos.logback.core.Appender;
2626
import ch.qos.logback.core.ConsoleAppender;
27-
import ch.qos.logback.core.rolling.FixedWindowRollingPolicy;
27+
import ch.qos.logback.core.CoreConstants;
2828
import ch.qos.logback.core.rolling.RollingFileAppender;
29-
import ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy;
29+
import ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy;
3030
import ch.qos.logback.core.util.FileSize;
3131
import ch.qos.logback.core.util.OptionHelper;
3232

@@ -45,6 +45,7 @@
4545
*
4646
* @author Phillip Webb
4747
* @author Madhura Bhave
48+
* @author Vedran Pavic
4849
* @since 1.1.2
4950
*/
5051
class DefaultLogbackConfiguration {
@@ -57,6 +58,8 @@ class DefaultLogbackConfiguration {
5758
private static final String FILE_LOG_PATTERN = "%d{yyyy-MM-dd HH:mm:ss.SSS} "
5859
+ "${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- [%t] %-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}";
5960

61+
private static final String MAX_FILE_SIZE = "10MB";
62+
6063
private static final Charset UTF8 = Charset.forName("UTF-8");
6164

6265
private final PropertyResolver patterns;
@@ -140,34 +143,35 @@ private Appender<ILoggingEvent> fileAppender(LogbackConfigurator config,
140143
config.start(encoder);
141144
appender.setFile(logFile);
142145
setRollingPolicy(appender, config, logFile);
143-
setMaxFileSize(appender, config);
144146
config.appender("FILE", appender);
145147
return appender;
146148
}
147149

148150
private void setRollingPolicy(RollingFileAppender<ILoggingEvent> appender,
149151
LogbackConfigurator config, String logFile) {
150-
FixedWindowRollingPolicy rollingPolicy = new FixedWindowRollingPolicy();
151-
rollingPolicy.setFileNamePattern(logFile + ".%i");
152+
SizeAndTimeBasedRollingPolicy<ILoggingEvent> rollingPolicy = new SizeAndTimeBasedRollingPolicy<>();
153+
rollingPolicy.setFileNamePattern(logFile + ".%d{yyyy-MM-dd}.%i.gz");
154+
setMaxFileSize(rollingPolicy,
155+
this.patterns.getProperty("logging.file.max-size", MAX_FILE_SIZE));
156+
rollingPolicy.setMaxHistory(this.patterns.getProperty("logging.file.max-history",
157+
Integer.class, CoreConstants.UNBOUND_HISTORY));
152158
appender.setRollingPolicy(rollingPolicy);
153159
rollingPolicy.setParent(appender);
154160
config.start(rollingPolicy);
155161
}
156162

157-
private void setMaxFileSize(RollingFileAppender<ILoggingEvent> appender,
158-
LogbackConfigurator config) {
159-
SizeBasedTriggeringPolicy<ILoggingEvent> triggeringPolicy = new SizeBasedTriggeringPolicy<>();
163+
private void setMaxFileSize(
164+
SizeAndTimeBasedRollingPolicy<ILoggingEvent> rollingPolicy,
165+
String maxFileSize) {
160166
try {
161-
triggeringPolicy.setMaxFileSize(FileSize.valueOf("10MB"));
167+
rollingPolicy.setMaxFileSize(FileSize.valueOf(maxFileSize));
162168
}
163169
catch (NoSuchMethodError ex) {
164170
// Logback < 1.1.8 used String configuration
165-
Method method = ReflectionUtils.findMethod(SizeBasedTriggeringPolicy.class,
166-
"setMaxFileSize", String.class);
167-
ReflectionUtils.invokeMethod(method, triggeringPolicy, "10MB");
171+
Method method = ReflectionUtils.findMethod(
172+
SizeAndTimeBasedRollingPolicy.class, "setMaxFileSize", String.class);
173+
ReflectionUtils.invokeMethod(method, rollingPolicy, maxFileSize);
168174
}
169-
appender.setTriggeringPolicy(triggeringPolicy);
170-
config.start(triggeringPolicy);
171175
}
172176

173177
}

spring-boot-project/spring-boot/src/main/resources/META-INF/additional-spring-configuration-metadata.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,20 @@
6969
"description": "Name of the log file. Names can be an exact location or relative to the current directory.",
7070
"sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener"
7171
},
72+
{
73+
"name": "logging.file.max-size",
74+
"type": "java.lang.String",
75+
"description": "Maximum log file size. Only supported with the default logback setup.",
76+
"sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener",
77+
"defaultValue": "10MB"
78+
},
79+
{
80+
"name": "logging.file.max-history",
81+
"type": "java.lang.Integer",
82+
"description": "Maximum number of archive log files to keep. Only supported with the default logback setup.",
83+
"sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener",
84+
"defaultValue": 0
85+
},
7286
{
7387
"name": "logging.level",
7488
"type": "java.util.Map<java.lang.String,java.lang.String>",

spring-boot-project/spring-boot/src/main/resources/org/springframework/boot/logging/logback/file-appender.xml

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,10 @@ initialization performed by Boot
1212
<pattern>${FILE_LOG_PATTERN}</pattern>
1313
</encoder>
1414
<file>${LOG_FILE}</file>
15-
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
16-
<fileNamePattern>${LOG_FILE}.%i</fileNamePattern>
15+
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
16+
<fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz</fileNamePattern>
17+
<maxFileSize>${MAX_FILE_SIZE:-10MB}</maxFileSize>
18+
<maxHistory>${MAX_HISTORY:-0}</maxHistory>
1719
</rollingPolicy>
18-
<triggeringPolicy
19-
class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
20-
<MaxFileSize>10MB</MaxFileSize>
21-
</triggeringPolicy>
2220
</appender>
2321
</included>

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemTests.java

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@
2727
import ch.qos.logback.classic.Logger;
2828
import ch.qos.logback.classic.LoggerContext;
2929
import ch.qos.logback.classic.spi.LoggerContextListener;
30+
import ch.qos.logback.core.ConsoleAppender;
31+
import ch.qos.logback.core.CoreConstants;
32+
import ch.qos.logback.core.rolling.RollingFileAppender;
33+
import ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy;
3034
import org.apache.commons.logging.Log;
3135
import org.apache.commons.logging.impl.SLF4JLogFactory;
3236
import org.hamcrest.Matcher;
@@ -48,6 +52,7 @@
4852
import org.springframework.boot.testsupport.assertj.Matched;
4953
import org.springframework.boot.testsupport.rule.OutputCapture;
5054
import org.springframework.mock.env.MockEnvironment;
55+
import org.springframework.test.util.ReflectionTestUtils;
5156
import org.springframework.util.FileCopyUtils;
5257
import org.springframework.util.StringUtils;
5358

@@ -66,6 +71,7 @@
6671
* @author Andy Wilkinson
6772
* @author Ben Hale
6873
* @author Madhura Bhave
74+
* @author Vedran Pavic
6975
*/
7076
public class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
7177

@@ -120,15 +126,16 @@ public void withFile() throws Exception {
120126
assertThat(getLineWithText(output, "Hello world")).contains("INFO");
121127
assertThat(file.exists()).isTrue();
122128
assertThat(getLineWithText(file, "Hello world")).contains("INFO");
129+
assertThat(ReflectionTestUtils.getField(getRollingPolicy(), "maxFileSize")
130+
.toString()).isEqualTo("10 MB");
131+
assertThat(getRollingPolicy().getMaxHistory())
132+
.isEqualTo(CoreConstants.UNBOUND_HISTORY);
123133
}
124134

125135
@Test
126136
public void testBasicConfigLocation() throws Exception {
127137
this.loggingSystem.beforeInitialize();
128-
ILoggerFactory factory = StaticLoggerBinder.getSingleton().getLoggerFactory();
129-
LoggerContext context = (LoggerContext) factory;
130-
Logger root = context.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);
131-
assertThat(root.getAppender("CONSOLE")).isNotNull();
138+
assertThat(getConsoleAppender()).isNotNull();
132139
}
133140

134141
@Test
@@ -339,6 +346,35 @@ public void testFilePatternProperty() throws Exception {
339346
assertThat(getLineWithText(file, "Hello world")).doesNotContain("INFO");
340347
}
341348

349+
@Test
350+
public void testMaxFileSizeProperty() throws Exception {
351+
MockEnvironment environment = new MockEnvironment();
352+
environment.setProperty("logging.file.max-size", "100MB");
353+
LoggingInitializationContext loggingInitializationContext = new LoggingInitializationContext(
354+
environment);
355+
File file = new File(tmpDir(), "logback-test.log");
356+
LogFile logFile = getLogFile(file.getPath(), null);
357+
this.loggingSystem.initialize(loggingInitializationContext, null, logFile);
358+
this.logger.info("Hello world");
359+
assertThat(getLineWithText(file, "Hello world")).contains("INFO");
360+
assertThat(ReflectionTestUtils.getField(getRollingPolicy(), "maxFileSize")
361+
.toString()).isEqualTo("100 MB");
362+
}
363+
364+
@Test
365+
public void testMaxHistoryProperty() throws Exception {
366+
MockEnvironment environment = new MockEnvironment();
367+
environment.setProperty("logging.file.max-history", "30");
368+
LoggingInitializationContext loggingInitializationContext = new LoggingInitializationContext(
369+
environment);
370+
File file = new File(tmpDir(), "logback-test.log");
371+
LogFile logFile = getLogFile(file.getPath(), null);
372+
this.loggingSystem.initialize(loggingInitializationContext, null, logFile);
373+
this.logger.info("Hello world");
374+
assertThat(getLineWithText(file, "Hello world")).contains("INFO");
375+
assertThat(getRollingPolicy().getMaxHistory()).isEqualTo(30);
376+
}
377+
342378
@Test
343379
public void exceptionsIncludeClassPackaging() throws Exception {
344380
this.loggingSystem.beforeInitialize();
@@ -404,6 +440,24 @@ public void initializationIsOnlyPerformedOnceUntilCleanedUp() throws Exception {
404440
verify(listener, times(2)).onReset(loggerContext);
405441
}
406442

443+
private static Logger getRootLogger() {
444+
ILoggerFactory factory = StaticLoggerBinder.getSingleton().getLoggerFactory();
445+
LoggerContext context = (LoggerContext) factory;
446+
return context.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);
447+
}
448+
449+
private static ConsoleAppender getConsoleAppender() {
450+
return (ConsoleAppender) getRootLogger().getAppender("CONSOLE");
451+
}
452+
453+
private static RollingFileAppender getFileAppender() {
454+
return (RollingFileAppender) getRootLogger().getAppender("FILE");
455+
}
456+
457+
private static SizeAndTimeBasedRollingPolicy getRollingPolicy() {
458+
return (SizeAndTimeBasedRollingPolicy) getFileAppender().getRollingPolicy();
459+
}
460+
407461
private String getLineWithText(File file, String outputSearch) throws Exception {
408462
return getLineWithText(FileCopyUtils.copyToString(new FileReader(file)),
409463
outputSearch);

0 commit comments

Comments
 (0)