Skip to content

Commit 7649f75

Browse files
committed
Add option to configure logging filters
This commit introduces `logging.filters.console` and `logging.filters.file` properties which allow configuration of logging filters for default console and file appenders.
1 parent b2839e2 commit 7649f75

File tree

4 files changed

+110
-4
lines changed

4 files changed

+110
-4
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.filters.console= # Comma-separated list of filter classes to apply to console appender. Only supported with the default logback setup.
45+
logging.filters.file= # Comma-separated list of filter classes to apply to file appender. 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/src/main/java/org/springframework/boot/logging/logback/DefaultLogbackConfiguration.java

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
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.OutputStreamAppender;
28+
import ch.qos.logback.core.filter.Filter;
2729
import ch.qos.logback.core.rolling.FixedWindowRollingPolicy;
2830
import ch.qos.logback.core.rolling.RollingFileAppender;
2931
import ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy;
@@ -45,6 +47,7 @@
4547
*
4648
* @author Phillip Webb
4749
* @author Madhura Bhave
50+
* @author Vedran Pavic
4851
* @since 1.1.2
4952
*/
5053
class DefaultLogbackConfiguration {
@@ -59,13 +62,13 @@ class DefaultLogbackConfiguration {
5962

6063
private static final Charset UTF8 = Charset.forName("UTF-8");
6164

62-
private final PropertyResolver patterns;
65+
private final PropertyResolver resolver;
6366

6467
private final LogFile logFile;
6568

6669
DefaultLogbackConfiguration(LoggingInitializationContext initializationContext,
6770
LogFile logFile) {
68-
this.patterns = getPatternsResolver(initializationContext.getEnvironment());
71+
this.resolver = getPatternsResolver(initializationContext.getEnvironment());
6972
this.logFile = logFile;
7073
}
7174

@@ -119,12 +122,17 @@ private void base(LogbackConfigurator config) {
119122
private Appender<ILoggingEvent> consoleAppender(LogbackConfigurator config) {
120123
ConsoleAppender<ILoggingEvent> appender = new ConsoleAppender<>();
121124
PatternLayoutEncoder encoder = new PatternLayoutEncoder();
122-
String logPattern = this.patterns.getProperty("logging.pattern.console",
125+
String logPattern = this.resolver.getProperty("logging.pattern.console",
123126
CONSOLE_LOG_PATTERN);
124127
encoder.setPattern(OptionHelper.substVars(logPattern, config.getContext()));
125128
encoder.setCharset(UTF8);
126129
config.start(encoder);
127130
appender.setEncoder(encoder);
131+
String[] filterClasses = this.resolver.getProperty("logging.filters.console",
132+
String[].class);
133+
if (filterClasses != null) {
134+
configureFilters(config, appender, filterClasses);
135+
}
128136
config.appender("CONSOLE", appender);
129137
return appender;
130138
}
@@ -133,14 +141,19 @@ private Appender<ILoggingEvent> fileAppender(LogbackConfigurator config,
133141
String logFile) {
134142
RollingFileAppender<ILoggingEvent> appender = new RollingFileAppender<>();
135143
PatternLayoutEncoder encoder = new PatternLayoutEncoder();
136-
String logPattern = this.patterns.getProperty("logging.pattern.file",
144+
String logPattern = this.resolver.getProperty("logging.pattern.file",
137145
FILE_LOG_PATTERN);
138146
encoder.setPattern(OptionHelper.substVars(logPattern, config.getContext()));
139147
appender.setEncoder(encoder);
140148
config.start(encoder);
141149
appender.setFile(logFile);
142150
setRollingPolicy(appender, config, logFile);
143151
setMaxFileSize(appender, config);
152+
String[] filterClasses = this.resolver.getProperty("logging.filters.file",
153+
String[].class);
154+
if (filterClasses != null) {
155+
configureFilters(config, appender, filterClasses);
156+
}
144157
config.appender("FILE", appender);
145158
return appender;
146159
}
@@ -170,4 +183,20 @@ private void setMaxFileSize(RollingFileAppender<ILoggingEvent> appender,
170183
config.start(triggeringPolicy);
171184
}
172185

186+
@SuppressWarnings("unchecked")
187+
private void configureFilters(LogbackConfigurator config,
188+
OutputStreamAppender appender, String[] filterClasses) {
189+
for (String filterClass : filterClasses) {
190+
try {
191+
Filter filter = (Filter) Class.forName(filterClass).newInstance();
192+
config.start(filter);
193+
appender.addFilter(filter);
194+
}
195+
catch (Exception e) {
196+
throw new IllegalStateException(
197+
"Unable to configure " + filterClass + " logging filter");
198+
}
199+
}
200+
}
201+
173202
}

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,18 @@
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.filter.console",
74+
"type": "java.lang.String[]",
75+
"description": "Comma-separated list of filter classes to apply to console appender. Only supported with the default logback setup.",
76+
"sourceType": "org.springframework.boot.logging.LoggingApplicationListener"
77+
},
78+
{
79+
"name": "logging.filter.file",
80+
"type": "java.lang.String[]",
81+
"description": "Comma-separated list of filter classes to apply to file appender. Only supported with the default logback setup.",
82+
"sourceType": "org.springframework.boot.logging.LoggingApplicationListener"
83+
},
7284
{
7385
"name": "logging.level",
7486
"type": "java.util.Map<java.lang.String,java.lang.String>",

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

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@
2626
import ch.qos.logback.classic.Level;
2727
import ch.qos.logback.classic.Logger;
2828
import ch.qos.logback.classic.LoggerContext;
29+
import ch.qos.logback.classic.spi.ILoggingEvent;
2930
import ch.qos.logback.classic.spi.LoggerContextListener;
31+
import ch.qos.logback.core.filter.Filter;
32+
import ch.qos.logback.core.spi.FilterReply;
3033
import org.apache.commons.logging.Log;
3134
import org.apache.commons.logging.impl.SLF4JLogFactory;
3235
import org.hamcrest.Matcher;
@@ -66,6 +69,7 @@
6669
* @author Andy Wilkinson
6770
* @author Ben Hale
6871
* @author Madhura Bhave
72+
* @author Vedran Pavic
6973
*/
7074
public class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
7175

@@ -404,6 +408,45 @@ public void initializationIsOnlyPerformedOnceUntilCleanedUp() throws Exception {
404408
verify(listener, times(2)).onReset(loggerContext);
405409
}
406410

411+
@Test
412+
public void consoleFiltersProperty() throws Exception {
413+
MockEnvironment environment = new MockEnvironment();
414+
environment.setProperty("logging.filters.console", HelloFilter.class.getName() +
415+
"," + HiFilter.class.getName());
416+
LoggingInitializationContext loggingInitializationContext =
417+
new LoggingInitializationContext(environment);
418+
this.loggingSystem.initialize(loggingInitializationContext, null, null);
419+
this.logger.info("Hello world");
420+
this.logger.info("Hi world");
421+
this.logger.info("Bye world");
422+
String output = this.output.toString().trim();
423+
assertThat(output).doesNotContain("Hello world");
424+
assertThat(output).doesNotContain("Hi world");
425+
assertThat(output).contains("Bye world");
426+
}
427+
428+
@Test
429+
public void fileFiltersProperty() throws Exception {
430+
MockEnvironment environment = new MockEnvironment();
431+
environment.setProperty("logging.filters.file", HelloFilter.class.getName() +
432+
"," + HiFilter.class.getName());
433+
LoggingInitializationContext loggingInitializationContext =
434+
new LoggingInitializationContext(environment);
435+
this.loggingSystem.initialize(loggingInitializationContext, null,
436+
getLogFile(null, tmpDir()));
437+
this.logger.info("Hello world");
438+
this.logger.info("Hi world");
439+
this.logger.info("Bye world");
440+
String output = this.output.toString().trim();
441+
File file = new File(tmpDir() + "/spring.log");
442+
assertThat(output).contains("Hello world");
443+
assertThat(output).contains("Hi world");
444+
assertThat(output).contains("Bye world");
445+
assertThat(getLineWithText(file, "Hello world")).isNull();
446+
assertThat(getLineWithText(file, "Hi world")).isNull();
447+
assertThat(getLineWithText(file, "Bye world")).isNotEmpty();
448+
}
449+
407450
private String getLineWithText(File file, String outputSearch) throws Exception {
408451
return getLineWithText(FileCopyUtils.copyToString(new FileReader(file)),
409452
outputSearch);
@@ -419,4 +462,24 @@ private String getLineWithText(String output, String outputSearch) {
419462
return null;
420463
}
421464

465+
static class HelloFilter extends Filter<ILoggingEvent> {
466+
467+
@Override
468+
public FilterReply decide(ILoggingEvent event) {
469+
return event.getMessage().contains("Hello")
470+
? FilterReply.DENY : FilterReply.NEUTRAL;
471+
}
472+
473+
}
474+
475+
static class HiFilter extends Filter<ILoggingEvent> {
476+
477+
@Override
478+
public FilterReply decide(ILoggingEvent event) {
479+
return event.getMessage().contains("Hi")
480+
? FilterReply.DENY : FilterReply.NEUTRAL;
481+
}
482+
483+
}
484+
422485
}

0 commit comments

Comments
 (0)