Skip to content

Commit 5cc640d

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 0e00a49 commit 5cc640d

File tree

4 files changed

+120
-8
lines changed

4 files changed

+120
-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: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2015 the original author or authors.
2+
* Copyright 2012-2016 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.
@@ -23,6 +23,8 @@
2323
import ch.qos.logback.classic.spi.ILoggingEvent;
2424
import ch.qos.logback.core.Appender;
2525
import ch.qos.logback.core.ConsoleAppender;
26+
import ch.qos.logback.core.OutputStreamAppender;
27+
import ch.qos.logback.core.filter.Filter;
2628
import ch.qos.logback.core.rolling.FixedWindowRollingPolicy;
2729
import ch.qos.logback.core.rolling.RollingFileAppender;
2830
import ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy;
@@ -41,6 +43,7 @@
4143
* and {@code file-appender.xml} files provided for classic {@code logback.xml} use.
4244
*
4345
* @author Phillip Webb
46+
* @author Vedran Pavic
4447
* @since 1.1.2
4548
*/
4649
class DefaultLogbackConfiguration {
@@ -55,21 +58,21 @@ class DefaultLogbackConfiguration {
5558

5659
private static final Charset UTF8 = Charset.forName("UTF-8");
5760

58-
private final PropertyResolver patterns;
61+
private final PropertyResolver resolver;
5962

6063
private final LogFile logFile;
6164

6265
DefaultLogbackConfiguration(LoggingInitializationContext initializationContext,
6366
LogFile logFile) {
64-
this.patterns = getPatternsResolver(initializationContext.getEnvironment());
67+
this.resolver = getPatternsResolver(initializationContext.getEnvironment());
6568
this.logFile = logFile;
6669
}
6770

6871
private PropertyResolver getPatternsResolver(Environment environment) {
6972
if (environment == null) {
7073
return new PropertySourcesPropertyResolver(null);
7174
}
72-
return new RelaxedPropertyResolver(environment, "logging.pattern.");
75+
return new RelaxedPropertyResolver(environment, "logging.");
7376
}
7477

7578
public void apply(LogbackConfigurator config) {
@@ -115,20 +118,29 @@ private void base(LogbackConfigurator config) {
115118
private Appender<ILoggingEvent> consoleAppender(LogbackConfigurator config) {
116119
ConsoleAppender<ILoggingEvent> appender = new ConsoleAppender<ILoggingEvent>();
117120
PatternLayoutEncoder encoder = new PatternLayoutEncoder();
118-
String logPattern = this.patterns.getProperty("console", CONSOLE_LOG_PATTERN);
121+
String logPattern = this.resolver.getProperty("pattern.console",
122+
CONSOLE_LOG_PATTERN);
119123
encoder.setPattern(OptionHelper.substVars(logPattern, config.getContext()));
120124
encoder.setCharset(UTF8);
121125
config.start(encoder);
122126
appender.setEncoder(encoder);
127+
128+
String[] filterClasses = this.resolver.getProperty("filters.console",
129+
String[].class);
130+
if (filterClasses != null) {
131+
configureFilters(config, appender, filterClasses);
132+
}
133+
123134
config.appender("CONSOLE", appender);
124135
return appender;
125136
}
126137

127138
private Appender<ILoggingEvent> fileAppender(LogbackConfigurator config,
128139
String logFile) {
129-
RollingFileAppender<ILoggingEvent> appender = new RollingFileAppender<ILoggingEvent>();
140+
RollingFileAppender<ILoggingEvent> appender =
141+
new RollingFileAppender<ILoggingEvent>();
130142
PatternLayoutEncoder encoder = new PatternLayoutEncoder();
131-
String logPattern = this.patterns.getProperty("file", FILE_LOG_PATTERN);
143+
String logPattern = this.resolver.getProperty("pattern.file", FILE_LOG_PATTERN);
132144
encoder.setPattern(OptionHelper.substVars(logPattern, config.getContext()));
133145
appender.setEncoder(encoder);
134146
config.start(encoder);
@@ -141,13 +153,36 @@ private Appender<ILoggingEvent> fileAppender(LogbackConfigurator config,
141153
rollingPolicy.setParent(appender);
142154
config.start(rollingPolicy);
143155

144-
SizeBasedTriggeringPolicy<ILoggingEvent> triggeringPolicy = new SizeBasedTriggeringPolicy<ILoggingEvent>();
156+
SizeBasedTriggeringPolicy<ILoggingEvent> triggeringPolicy =
157+
new SizeBasedTriggeringPolicy<ILoggingEvent>();
145158
triggeringPolicy.setMaxFileSize("10MB");
146159
appender.setTriggeringPolicy(triggeringPolicy);
147160
config.start(triggeringPolicy);
148161

162+
String[] filterClasses = this.resolver.getProperty("filters.file",
163+
String[].class);
164+
if (filterClasses != null) {
165+
configureFilters(config, appender, filterClasses);
166+
}
167+
149168
config.appender("FILE", appender);
150169
return appender;
151170
}
152171

172+
@SuppressWarnings("unchecked")
173+
private void configureFilters(LogbackConfigurator config,
174+
OutputStreamAppender appender, String[] filterClasses) {
175+
for (String filterClass : filterClasses) {
176+
try {
177+
Filter filter = (Filter) Class.forName(filterClass).newInstance();
178+
config.start(filter);
179+
appender.addFilter(filter);
180+
}
181+
catch (Exception e) {
182+
throw new IllegalStateException("Unable to configure " + filterClass +
183+
" logging filter");
184+
}
185+
}
186+
}
187+
153188
}

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: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@
2323

2424
import ch.qos.logback.classic.Logger;
2525
import ch.qos.logback.classic.LoggerContext;
26+
import ch.qos.logback.classic.spi.ILoggingEvent;
2627
import ch.qos.logback.classic.spi.LoggerContextListener;
28+
import ch.qos.logback.core.filter.Filter;
29+
import ch.qos.logback.core.spi.FilterReply;
2730
import org.apache.commons.logging.Log;
2831
import org.apache.commons.logging.impl.SLF4JLogFactory;
2932
import org.hamcrest.Matcher;
@@ -59,6 +62,7 @@
5962
* @author Dave Syer
6063
* @author Phillip Webb
6164
* @author Andy Wilkinson
65+
* @author Vedran Pavic
6266
*/
6367
public class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
6468

@@ -337,6 +341,45 @@ public void initializationIsOnlyPerformedOnceUntilCleanedUp() throws Exception {
337341
verify(listener, times(2)).onReset(loggerContext);
338342
}
339343

344+
@Test
345+
public void consoleFiltersProperty() throws Exception {
346+
MockEnvironment environment = new MockEnvironment();
347+
environment.setProperty("logging.filters.console", HelloFilter.class.getName() +
348+
"," + HiFilter.class.getName());
349+
LoggingInitializationContext loggingInitializationContext =
350+
new LoggingInitializationContext(environment);
351+
this.loggingSystem.initialize(loggingInitializationContext, null, null);
352+
this.logger.info("Hello world");
353+
this.logger.info("Hi world");
354+
this.logger.info("Bye world");
355+
String output = this.output.toString().trim();
356+
assertThat(output).doesNotContain("Hello world");
357+
assertThat(output).doesNotContain("Hi world");
358+
assertThat(output).contains("Bye world");
359+
}
360+
361+
@Test
362+
public void fileFiltersProperty() throws Exception {
363+
MockEnvironment environment = new MockEnvironment();
364+
environment.setProperty("logging.filters.file", HelloFilter.class.getName() +
365+
"," + HiFilter.class.getName());
366+
LoggingInitializationContext loggingInitializationContext =
367+
new LoggingInitializationContext(environment);
368+
this.loggingSystem.initialize(loggingInitializationContext, null,
369+
getLogFile(null, tmpDir()));
370+
this.logger.info("Hello world");
371+
this.logger.info("Hi world");
372+
this.logger.info("Bye world");
373+
String output = this.output.toString().trim();
374+
File file = new File(tmpDir() + "/spring.log");
375+
assertThat(output).contains("Hello world");
376+
assertThat(output).contains("Hi world");
377+
assertThat(output).contains("Bye world");
378+
assertThat(getLineWithText(file, "Hello world")).isNull();
379+
assertThat(getLineWithText(file, "Hi world")).isNull();
380+
assertThat(getLineWithText(file, "Bye world")).isNotEmpty();
381+
}
382+
340383
private String getLineWithText(File file, String outputSearch) throws Exception {
341384
return getLineWithText(FileCopyUtils.copyToString(new FileReader(file)),
342385
outputSearch);
@@ -352,4 +395,24 @@ private String getLineWithText(String output, String outputSearch) {
352395
return null;
353396
}
354397

398+
static class HelloFilter extends Filter<ILoggingEvent> {
399+
400+
@Override
401+
public FilterReply decide(ILoggingEvent event) {
402+
return event.getMessage().contains("Hello")
403+
? FilterReply.DENY : FilterReply.NEUTRAL;
404+
}
405+
406+
}
407+
408+
static class HiFilter extends Filter<ILoggingEvent> {
409+
410+
@Override
411+
public FilterReply decide(ILoggingEvent event) {
412+
return event.getMessage().contains("Hi")
413+
? FilterReply.DENY : FilterReply.NEUTRAL;
414+
}
415+
416+
}
417+
355418
}

0 commit comments

Comments
 (0)