Skip to content

Commit e3f8ea8

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 7253bb3 commit e3f8ea8

File tree

4 files changed

+116
-8
lines changed

4 files changed

+116
-8
lines changed

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/src/main/java/org/springframework/boot/logging/logback/DefaultLogbackConfiguration.java

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2016 the original author or authors.
2+
* Copyright 2012-2017 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -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;
@@ -44,6 +46,7 @@
4446
* and {@code file-appender.xml} files provided for classic {@code logback.xml} use.
4547
*
4648
* @author Phillip Webb
49+
* @author Vedran Pavic
4750
* @since 1.1.2
4851
*/
4952
class DefaultLogbackConfiguration {
@@ -58,13 +61,13 @@ class DefaultLogbackConfiguration {
5861

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

61-
private final PropertyResolver patterns;
64+
private final PropertyResolver resolver;
6265

6366
private final LogFile logFile;
6467

6568
DefaultLogbackConfiguration(LoggingInitializationContext initializationContext,
6669
LogFile logFile) {
67-
this.patterns = getPatternsResolver(initializationContext.getEnvironment());
70+
this.resolver = getPatternsResolver(initializationContext.getEnvironment());
6871
this.logFile = logFile;
6972
}
7073

@@ -73,7 +76,7 @@ private PropertyResolver getPatternsResolver(Environment environment) {
7376
return new PropertySourcesPropertyResolver(null);
7477
}
7578
return RelaxedPropertyResolver.ignoringUnresolvableNestedPlaceholders(environment,
76-
"logging.pattern.");
79+
"logging.");
7780
}
7881

7982
public void apply(LogbackConfigurator config) {
@@ -114,26 +117,38 @@ private void base(LogbackConfigurator config) {
114117
private Appender<ILoggingEvent> consoleAppender(LogbackConfigurator config) {
115118
ConsoleAppender<ILoggingEvent> appender = new ConsoleAppender<ILoggingEvent>();
116119
PatternLayoutEncoder encoder = new PatternLayoutEncoder();
117-
String logPattern = this.patterns.getProperty("console", CONSOLE_LOG_PATTERN);
120+
String logPattern = this.resolver.getProperty("pattern.console",
121+
CONSOLE_LOG_PATTERN);
118122
encoder.setPattern(OptionHelper.substVars(logPattern, config.getContext()));
119123
encoder.setCharset(UTF8);
120124
config.start(encoder);
121125
appender.setEncoder(encoder);
126+
String[] filterClasses = this.resolver.getProperty("filters.console",
127+
String[].class);
128+
if (filterClasses != null) {
129+
configureFilters(config, appender, filterClasses);
130+
}
122131
config.appender("CONSOLE", appender);
123132
return appender;
124133
}
125134

126135
private Appender<ILoggingEvent> fileAppender(LogbackConfigurator config,
127136
String logFile) {
128-
RollingFileAppender<ILoggingEvent> appender = new RollingFileAppender<ILoggingEvent>();
137+
RollingFileAppender<ILoggingEvent> appender =
138+
new RollingFileAppender<ILoggingEvent>();
129139
PatternLayoutEncoder encoder = new PatternLayoutEncoder();
130-
String logPattern = this.patterns.getProperty("file", FILE_LOG_PATTERN);
140+
String logPattern = this.resolver.getProperty("pattern.file", FILE_LOG_PATTERN);
131141
encoder.setPattern(OptionHelper.substVars(logPattern, config.getContext()));
132142
appender.setEncoder(encoder);
133143
config.start(encoder);
134144
appender.setFile(logFile);
135145
setRollingPolicy(appender, config, logFile);
136146
setMaxFileSize(appender, config);
147+
String[] filterClasses = this.resolver.getProperty("filters.file",
148+
String[].class);
149+
if (filterClasses != null) {
150+
configureFilters(config, appender, filterClasses);
151+
}
137152
config.appender("FILE", appender);
138153
return appender;
139154
}
@@ -163,4 +178,20 @@ private void setMaxFileSize(RollingFileAppender<ILoggingEvent> appender,
163178
config.start(triggeringPolicy);
164179
}
165180

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

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.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/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemTests.java

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2016 the original author or authors.
2+
* Copyright 2012-2017 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -25,7 +25,10 @@
2525

2626
import ch.qos.logback.classic.Logger;
2727
import ch.qos.logback.classic.LoggerContext;
28+
import ch.qos.logback.classic.spi.ILoggingEvent;
2829
import ch.qos.logback.classic.spi.LoggerContextListener;
30+
import ch.qos.logback.core.filter.Filter;
31+
import ch.qos.logback.core.spi.FilterReply;
2932
import org.apache.commons.logging.Log;
3033
import org.apache.commons.logging.impl.SLF4JLogFactory;
3134
import org.hamcrest.Matcher;
@@ -64,6 +67,7 @@
6467
* @author Phillip Webb
6568
* @author Andy Wilkinson
6669
* @author Ben Hale
70+
* @author Vedran Pavic
6771
*/
6872
public class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
6973

@@ -372,6 +376,45 @@ public void initializationIsOnlyPerformedOnceUntilCleanedUp() throws Exception {
372376
verify(listener, times(2)).onReset(loggerContext);
373377
}
374378

379+
@Test
380+
public void consoleFiltersProperty() throws Exception {
381+
MockEnvironment environment = new MockEnvironment();
382+
environment.setProperty("logging.filters.console", HelloFilter.class.getName() +
383+
"," + HiFilter.class.getName());
384+
LoggingInitializationContext loggingInitializationContext =
385+
new LoggingInitializationContext(environment);
386+
this.loggingSystem.initialize(loggingInitializationContext, null, null);
387+
this.logger.info("Hello world");
388+
this.logger.info("Hi world");
389+
this.logger.info("Bye world");
390+
String output = this.output.toString().trim();
391+
assertThat(output).doesNotContain("Hello world");
392+
assertThat(output).doesNotContain("Hi world");
393+
assertThat(output).contains("Bye world");
394+
}
395+
396+
@Test
397+
public void fileFiltersProperty() throws Exception {
398+
MockEnvironment environment = new MockEnvironment();
399+
environment.setProperty("logging.filters.file", HelloFilter.class.getName() +
400+
"," + HiFilter.class.getName());
401+
LoggingInitializationContext loggingInitializationContext =
402+
new LoggingInitializationContext(environment);
403+
this.loggingSystem.initialize(loggingInitializationContext, null,
404+
getLogFile(null, tmpDir()));
405+
this.logger.info("Hello world");
406+
this.logger.info("Hi world");
407+
this.logger.info("Bye world");
408+
String output = this.output.toString().trim();
409+
File file = new File(tmpDir() + "/spring.log");
410+
assertThat(output).contains("Hello world");
411+
assertThat(output).contains("Hi world");
412+
assertThat(output).contains("Bye world");
413+
assertThat(getLineWithText(file, "Hello world")).isNull();
414+
assertThat(getLineWithText(file, "Hi world")).isNull();
415+
assertThat(getLineWithText(file, "Bye world")).isNotEmpty();
416+
}
417+
375418
private String getLineWithText(File file, String outputSearch) throws Exception {
376419
return getLineWithText(FileCopyUtils.copyToString(new FileReader(file)),
377420
outputSearch);
@@ -387,4 +430,24 @@ private String getLineWithText(String output, String outputSearch) {
387430
return null;
388431
}
389432

433+
static class HelloFilter extends Filter<ILoggingEvent> {
434+
435+
@Override
436+
public FilterReply decide(ILoggingEvent event) {
437+
return event.getMessage().contains("Hello")
438+
? FilterReply.DENY : FilterReply.NEUTRAL;
439+
}
440+
441+
}
442+
443+
static class HiFilter extends Filter<ILoggingEvent> {
444+
445+
@Override
446+
public FilterReply decide(ILoggingEvent event) {
447+
return event.getMessage().contains("Hi")
448+
? FilterReply.DENY : FilterReply.NEUTRAL;
449+
}
450+
451+
}
452+
390453
}

0 commit comments

Comments
 (0)