From f3f562f3866c1c09ebac9c55e711b68a579388e5 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Fri, 26 Jun 2015 13:34:35 -0700 Subject: [PATCH] Support in logback configurations Include support for a new element which can be used in `logback-spring.xml` files to selectively enable or disable parts of the configuration. For example: ... ... Fixes gh-3338 --- .../src/main/resources/logback-spring.xml | 7 +- .../SampleLogbackApplicationTests.java | 8 ++ .../boot/logging/AbstractLoggingSystem.java | 28 ++-- .../logging/LoggingApplicationListener.java | 6 +- .../logging/LoggingInitializationContext.java | 48 +++++++ .../boot/logging/LoggingSystem.java | 8 +- .../boot/logging/java/JavaLoggingSystem.java | 6 + .../logging/log4j/Log4JLoggingSystem.java | 8 +- .../logging/log4j2/Log4J2LoggingSystem.java | 13 +- .../logging/logback/LogbackLoggingSystem.java | 41 ++++-- .../logback/SpringBootJoranConfigurator.java | 48 +++++++ .../logging/logback/SpringProfileAction.java | 110 +++++++++++++++ .../logging/java/JavaLoggerSystemTests.java | 15 ++- .../log4j/Log4JLoggingSystemTests.java | 12 +- .../log4j2/Log4J2LoggingSystemTests.java | 12 +- .../logback/LogbackLoggingSystemTests.java | 21 ++- .../SpringBootJoranConfiguratorTests.java | 125 ++++++++++++++++++ .../boot/logging/logback/nested.xml | 9 ++ .../logging/logback/production-profile.xml | 7 + 19 files changed, 476 insertions(+), 56 deletions(-) create mode 100644 spring-boot/src/main/java/org/springframework/boot/logging/LoggingInitializationContext.java create mode 100644 spring-boot/src/main/java/org/springframework/boot/logging/logback/SpringBootJoranConfigurator.java create mode 100644 spring-boot/src/main/java/org/springframework/boot/logging/logback/SpringProfileAction.java create mode 100644 spring-boot/src/test/java/org/springframework/boot/logging/logback/SpringBootJoranConfiguratorTests.java create mode 100644 spring-boot/src/test/resources/org/springframework/boot/logging/logback/nested.xml create mode 100644 spring-boot/src/test/resources/org/springframework/boot/logging/logback/production-profile.xml diff --git a/spring-boot-samples/spring-boot-sample-logback/src/main/resources/logback-spring.xml b/spring-boot-samples/spring-boot-sample-logback/src/main/resources/logback-spring.xml index 3761cc62b63..e279b822768 100644 --- a/spring-boot-samples/spring-boot-sample-logback/src/main/resources/logback-spring.xml +++ b/spring-boot-samples/spring-boot-sample-logback/src/main/resources/logback-spring.xml @@ -1,5 +1,8 @@ - - + + + + + diff --git a/spring-boot-samples/spring-boot-sample-logback/src/test/java/sample/logback/SampleLogbackApplicationTests.java b/spring-boot-samples/spring-boot-sample-logback/src/test/java/sample/logback/SampleLogbackApplicationTests.java index b452efa7c4e..aa9b938c6c6 100644 --- a/spring-boot-samples/spring-boot-sample-logback/src/test/java/sample/logback/SampleLogbackApplicationTests.java +++ b/spring-boot-samples/spring-boot-sample-logback/src/test/java/sample/logback/SampleLogbackApplicationTests.java @@ -35,4 +35,12 @@ public class SampleLogbackApplicationTests { this.outputCapture.expect(not(containsString("Sample Trace Message"))); } + @Test + public void testProfile() throws Exception { + SampleLogbackApplication + .main(new String[] { "--spring.profiles.active=staging" }); + this.outputCapture.expect(containsString("Sample Debug Message")); + this.outputCapture.expect(containsString("Sample Trace Message")); + } + } diff --git a/spring-boot/src/main/java/org/springframework/boot/logging/AbstractLoggingSystem.java b/spring-boot/src/main/java/org/springframework/boot/logging/AbstractLoggingSystem.java index 9cce7b176bc..6e587ed2f72 100644 --- a/spring-boot/src/main/java/org/springframework/boot/logging/AbstractLoggingSystem.java +++ b/spring-boot/src/main/java/org/springframework/boot/logging/AbstractLoggingSystem.java @@ -40,31 +40,35 @@ public abstract class AbstractLoggingSystem extends LoggingSystem { } @Override - public void initialize(String configLocation, LogFile logFile) { + public void initialize(LoggingInitializationContext initializationContext, + String configLocation, LogFile logFile) { if (StringUtils.hasLength(configLocation)) { - initializeWithSpecificConfig(configLocation, logFile); + initializeWithSpecificConfig(initializationContext, configLocation, logFile); return; } - initializeWithConventions(logFile); + initializeWithConventions(initializationContext, logFile); } - private void initializeWithSpecificConfig(String configLocation, LogFile logFile) { + private void initializeWithSpecificConfig( + LoggingInitializationContext initializationContext, String configLocation, + LogFile logFile) { configLocation = SystemPropertyUtils.resolvePlaceholders(configLocation); - loadConfiguration(configLocation, logFile); + loadConfiguration(initializationContext, configLocation, logFile); } - private void initializeWithConventions(LogFile logFile) { + private void initializeWithConventions( + LoggingInitializationContext initializationContext, LogFile logFile) { String config = getSelfInitializationConfig(); if (config != null && logFile == null) { // self initialization has occurred, reinitialize in case of property changes - reinitialize(); + reinitialize(initializationContext); return; } if (config == null) { config = getSpringInitializationConfig(); } if (config != null) { - loadConfiguration(config, logFile); + loadConfiguration(initializationContext, config, logFile); return; } loadDefaults(logFile); @@ -131,18 +135,22 @@ public abstract class AbstractLoggingSystem extends LoggingSystem { /** * Load a specific configuration. + * @param initializationContext the logging initialization context * @param location the location of the configuration to load (never {@code null}) * @param logFile the file to load or {@code null} if no log file is to be written */ - protected abstract void loadConfiguration(String location, LogFile logFile); + protected abstract void loadConfiguration( + LoggingInitializationContext initializationContext, String location, + LogFile logFile); /** * Reinitialize the logging system if required. Called when * {@link #getSelfInitializationConfig()} is used and the log file hasn't changed. May * be used to reload configuration (for example to pickup additional System * properties). + * @param initializationContext the logging initialization context */ - protected void reinitialize() { + protected void reinitialize(LoggingInitializationContext initializationContext) { } protected final ClassLoader getClassLoader() { diff --git a/spring-boot/src/main/java/org/springframework/boot/logging/LoggingApplicationListener.java b/spring-boot/src/main/java/org/springframework/boot/logging/LoggingApplicationListener.java index 8ef047903a7..73bc27a5adc 100644 --- a/spring-boot/src/main/java/org/springframework/boot/logging/LoggingApplicationListener.java +++ b/spring-boot/src/main/java/org/springframework/boot/logging/LoggingApplicationListener.java @@ -198,12 +198,14 @@ public class LoggingApplicationListener implements GenericApplicationListener { private void initializeSystem(ConfigurableEnvironment environment, LoggingSystem system) { + LoggingInitializationContext initializationContext = new LoggingInitializationContext( + environment); LogFile logFile = LogFile.get(environment); String logConfig = environment.getProperty(CONFIG_PROPERTY); if (StringUtils.hasLength(logConfig)) { try { ResourceUtils.getURL(logConfig).openStream().close(); - system.initialize(logConfig, logFile); + system.initialize(initializationContext, logConfig, logFile); } catch (Exception ex) { // NOTE: We can't use the logger here to report the problem @@ -214,7 +216,7 @@ public class LoggingApplicationListener implements GenericApplicationListener { } } else { - system.initialize(null, logFile); + system.initialize(initializationContext, null, logFile); } } diff --git a/spring-boot/src/main/java/org/springframework/boot/logging/LoggingInitializationContext.java b/spring-boot/src/main/java/org/springframework/boot/logging/LoggingInitializationContext.java new file mode 100644 index 00000000000..586b5eee392 --- /dev/null +++ b/spring-boot/src/main/java/org/springframework/boot/logging/LoggingInitializationContext.java @@ -0,0 +1,48 @@ +/* + * Copyright 2012-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.logging; + +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.Environment; + +/** + * Context passed to the {@link LoggingSystem} during initialization. + * + * @author Phillip Webb + * @since 1.3.0 + */ +public class LoggingInitializationContext { + + private final ConfigurableEnvironment environment; + + /** + * Create a new {@link LoggingInitializationContext} instance. + * @param environment the Spring environment. + */ + public LoggingInitializationContext(ConfigurableEnvironment environment) { + this.environment = environment; + } + + /** + * Return the Spring environment if available. + * @return the {@link Environment} or {@code null} + */ + public Environment getEnvironment() { + return this.environment; + } + +} diff --git a/spring-boot/src/main/java/org/springframework/boot/logging/LoggingSystem.java b/spring-boot/src/main/java/org/springframework/boot/logging/LoggingSystem.java index 1210f151da9..5e6871ad251 100644 --- a/spring-boot/src/main/java/org/springframework/boot/logging/LoggingSystem.java +++ b/spring-boot/src/main/java/org/springframework/boot/logging/LoggingSystem.java @@ -53,19 +53,21 @@ public abstract class LoggingSystem { /** * Reset the logging system to be limit output. This method may be called before - * {@link #initialize(String, LogFile)} to reduce logging noise until the system has - * been fully Initialized. + * {@link #initialize(LoggingInitializationContext, String, LogFile)} to reduce + * logging noise until the system has been fully Initialized. */ public abstract void beforeInitialize(); /** * Fully initialize the logging system. + * @param initializationContext the logging initialization context * @param configLocation a log configuration location or {@code null} if default * initialization is required * @param logFile the log output file that should be written or {@code null} for * console only output */ - public abstract void initialize(String configLocation, LogFile logFile); + public abstract void initialize(LoggingInitializationContext initializationContext, + String configLocation, LogFile logFile); /** * Clean up the logging system. The default implementation does nothing. Subclasses diff --git a/spring-boot/src/main/java/org/springframework/boot/logging/java/JavaLoggingSystem.java b/spring-boot/src/main/java/org/springframework/boot/logging/java/JavaLoggingSystem.java index 6bd3ba8e0d6..ff662ba5cb6 100644 --- a/spring-boot/src/main/java/org/springframework/boot/logging/java/JavaLoggingSystem.java +++ b/spring-boot/src/main/java/org/springframework/boot/logging/java/JavaLoggingSystem.java @@ -28,6 +28,7 @@ import java.util.logging.Logger; import org.springframework.boot.logging.AbstractLoggingSystem; import org.springframework.boot.logging.LogFile; import org.springframework.boot.logging.LogLevel; +import org.springframework.boot.logging.LoggingInitializationContext; import org.springframework.boot.logging.LoggingSystem; import org.springframework.util.Assert; import org.springframework.util.FileCopyUtils; @@ -81,6 +82,11 @@ public class JavaLoggingSystem extends AbstractLoggingSystem { } @Override + protected void loadConfiguration(LoggingInitializationContext initializationContext, + String location, LogFile logFile) { + loadConfiguration(location, logFile); + } + protected void loadConfiguration(String location, LogFile logFile) { Assert.notNull(location, "Location must not be null"); try { diff --git a/spring-boot/src/main/java/org/springframework/boot/logging/log4j/Log4JLoggingSystem.java b/spring-boot/src/main/java/org/springframework/boot/logging/log4j/Log4JLoggingSystem.java index 2820fe96dd6..96af7c1a255 100644 --- a/spring-boot/src/main/java/org/springframework/boot/logging/log4j/Log4JLoggingSystem.java +++ b/spring-boot/src/main/java/org/springframework/boot/logging/log4j/Log4JLoggingSystem.java @@ -25,6 +25,7 @@ import org.apache.log4j.LogManager; import org.apache.log4j.Logger; import org.springframework.boot.logging.LogFile; import org.springframework.boot.logging.LogLevel; +import org.springframework.boot.logging.LoggingInitializationContext; import org.springframework.boot.logging.LoggingSystem; import org.springframework.boot.logging.Slf4JLoggingSystem; import org.springframework.util.Assert; @@ -79,6 +80,11 @@ public class Log4JLoggingSystem extends Slf4JLoggingSystem { } @Override + protected void loadConfiguration(LoggingInitializationContext initializationContext, + String location, LogFile logFile) { + loadConfiguration(location, logFile); + } + protected void loadConfiguration(String location, LogFile logFile) { Assert.notNull(location, "Location must not be null"); if (logFile != null) { @@ -94,7 +100,7 @@ public class Log4JLoggingSystem extends Slf4JLoggingSystem { } @Override - protected void reinitialize() { + protected void reinitialize(LoggingInitializationContext initializationContext) { loadConfiguration(getSelfInitializationConfig(), null); } diff --git a/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystem.java b/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystem.java index 04a188bb883..57578be33a9 100644 --- a/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystem.java +++ b/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystem.java @@ -39,6 +39,7 @@ import org.apache.logging.log4j.core.filter.AbstractFilter; import org.apache.logging.log4j.message.Message; import org.springframework.boot.logging.LogFile; import org.springframework.boot.logging.LogLevel; +import org.springframework.boot.logging.LoggingInitializationContext; import org.springframework.boot.logging.LoggingSystem; import org.springframework.boot.logging.Slf4JLoggingSystem; import org.springframework.util.Assert; @@ -128,9 +129,10 @@ public class Log4J2LoggingSystem extends Slf4JLoggingSystem { } @Override - public void initialize(String configLocation, LogFile logFile) { + public void initialize(LoggingInitializationContext initializationContext, + String configLocation, LogFile logFile) { getLoggerConfig(null).removeFilter(FILTER); - super.initialize(configLocation, logFile); + super.initialize(initializationContext, configLocation, logFile); } @Override @@ -144,6 +146,11 @@ public class Log4J2LoggingSystem extends Slf4JLoggingSystem { } @Override + protected void loadConfiguration(LoggingInitializationContext initializationContext, + String location, LogFile logFile) { + loadConfiguration(location, logFile); + } + protected void loadConfiguration(String location, LogFile logFile) { Assert.notNull(location, "Location must not be null"); if (logFile != null) { @@ -170,7 +177,7 @@ public class Log4J2LoggingSystem extends Slf4JLoggingSystem { } @Override - protected void reinitialize() { + protected void reinitialize(LoggingInitializationContext initializationContext) { getLoggerContext().reconfigure(); } diff --git a/spring-boot/src/main/java/org/springframework/boot/logging/logback/LogbackLoggingSystem.java b/spring-boot/src/main/java/org/springframework/boot/logging/logback/LogbackLoggingSystem.java index 52a10a8e4e2..c4ccc1e6601 100644 --- a/spring-boot/src/main/java/org/springframework/boot/logging/logback/LogbackLoggingSystem.java +++ b/spring-boot/src/main/java/org/springframework/boot/logging/logback/LogbackLoggingSystem.java @@ -30,6 +30,7 @@ import org.slf4j.Marker; import org.slf4j.impl.StaticLoggerBinder; import org.springframework.boot.logging.LogFile; import org.springframework.boot.logging.LogLevel; +import org.springframework.boot.logging.LoggingInitializationContext; import org.springframework.boot.logging.LoggingSystem; import org.springframework.boot.logging.Slf4JLoggingSystem; import org.springframework.util.Assert; @@ -38,8 +39,10 @@ import org.springframework.util.StringUtils; import ch.qos.logback.classic.Level; import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.joran.JoranConfigurator; import ch.qos.logback.classic.turbo.TurboFilter; import ch.qos.logback.classic.util.ContextInitializer; +import ch.qos.logback.core.joran.spi.JoranException; import ch.qos.logback.core.spi.FilterReply; import ch.qos.logback.core.status.Status; @@ -93,9 +96,10 @@ public class LogbackLoggingSystem extends Slf4JLoggingSystem { } @Override - public void initialize(String configLocation, LogFile logFile) { + public void initialize(LoggingInitializationContext initializationContext, + String configLocation, LogFile logFile) { getLogger(null).getLoggerContext().getTurboFilterList().remove(FILTER); - super.initialize(configLocation, logFile); + super.initialize(initializationContext, configLocation, logFile); } @Override @@ -108,23 +112,24 @@ public class LogbackLoggingSystem extends Slf4JLoggingSystem { } @Override - protected void loadConfiguration(String location, LogFile logFile) { + protected void loadConfiguration(LoggingInitializationContext initializationContext, + String location, LogFile logFile) { Assert.notNull(location, "Location must not be null"); if (logFile != null) { logFile.applyToSystemProperties(); } - LoggerContext context = getLoggerContext(); - context.stop(); - context.reset(); + LoggerContext loggerContext = getLoggerContext(); + loggerContext.stop(); + loggerContext.reset(); try { - URL url = ResourceUtils.getURL(location); - new ContextInitializer(context).configureByResource(url); + configureByResourceUrl(initializationContext, loggerContext, + ResourceUtils.getURL(location)); } catch (Exception ex) { throw new IllegalStateException("Could not initialize Logback logging from " + location, ex); } - List statuses = context.getStatusManager().getCopyOfStatusList(); + List statuses = loggerContext.getStatusManager().getCopyOfStatusList(); StringBuilder errors = new StringBuilder(); for (Status status : statuses) { if (status.getLevel() == Status.ERROR) { @@ -138,6 +143,20 @@ public class LogbackLoggingSystem extends Slf4JLoggingSystem { } } + private void configureByResourceUrl( + LoggingInitializationContext initializationContext, + LoggerContext loggerContext, URL url) throws JoranException { + if (url.toString().endsWith("xml")) { + JoranConfigurator configurator = new SpringBootJoranConfigurator( + initializationContext); + configurator.setContext(loggerContext); + configurator.doConfigure(url); + } + else { + new ContextInitializer(loggerContext).configureByResource(url); + } + } + @Override public void cleanUp() { super.cleanUp(); @@ -145,9 +164,9 @@ public class LogbackLoggingSystem extends Slf4JLoggingSystem { } @Override - protected void reinitialize() { + protected void reinitialize(LoggingInitializationContext initializationContext) { getLoggerContext().reset(); - loadConfiguration(getSelfInitializationConfig(), null); + loadConfiguration(initializationContext, getSelfInitializationConfig(), null); } private void configureJBossLoggingToUseSlf4j() { diff --git a/spring-boot/src/main/java/org/springframework/boot/logging/logback/SpringBootJoranConfigurator.java b/spring-boot/src/main/java/org/springframework/boot/logging/logback/SpringBootJoranConfigurator.java new file mode 100644 index 00000000000..6ef15c5b8ca --- /dev/null +++ b/spring-boot/src/main/java/org/springframework/boot/logging/logback/SpringBootJoranConfigurator.java @@ -0,0 +1,48 @@ +/* + * Copyright 2012-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.logging.logback; + +import org.springframework.boot.logging.LoggingInitializationContext; + +import ch.qos.logback.classic.joran.JoranConfigurator; +import ch.qos.logback.core.joran.action.NOPAction; +import ch.qos.logback.core.joran.spi.ElementSelector; +import ch.qos.logback.core.joran.spi.RuleStore; + +/** + * Extended version of the Logback {@link JoranConfigurator} that adds additional Spring + * Boot rules. + * + * @author Phillip Webb + */ +class SpringBootJoranConfigurator extends JoranConfigurator { + + private LoggingInitializationContext initializationContext; + + public SpringBootJoranConfigurator(LoggingInitializationContext initializationContext) { + this.initializationContext = initializationContext; + } + + @Override + public void addInstanceRules(RuleStore rs) { + super.addInstanceRules(rs); + rs.addRule(new ElementSelector("*/springProfile"), new SpringProfileAction( + this.initializationContext.getEnvironment())); + rs.addRule(new ElementSelector("*/springProfile/*"), new NOPAction()); + } + +} diff --git a/spring-boot/src/main/java/org/springframework/boot/logging/logback/SpringProfileAction.java b/spring-boot/src/main/java/org/springframework/boot/logging/logback/SpringProfileAction.java new file mode 100644 index 00000000000..101c206630e --- /dev/null +++ b/spring-boot/src/main/java/org/springframework/boot/logging/logback/SpringProfileAction.java @@ -0,0 +1,110 @@ +/* + * Copyright 2012-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.logging.logback; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.core.env.Environment; +import org.springframework.util.Assert; +import org.xml.sax.Attributes; + +import ch.qos.logback.core.joran.action.Action; +import ch.qos.logback.core.joran.event.InPlayListener; +import ch.qos.logback.core.joran.event.SaxEvent; +import ch.qos.logback.core.joran.spi.ActionException; +import ch.qos.logback.core.joran.spi.InterpretationContext; +import ch.qos.logback.core.joran.spi.Interpreter; +import ch.qos.logback.core.util.OptionHelper; + +/** + * Lockback {@link Action} to support {@code } tags. Allows section of a + * logback configuration to only be enabled when a specific profile is active. + * + * @author Phillip Webb + */ +class SpringProfileAction extends Action implements InPlayListener { + + private final Environment environment; + + private int depth = 0; + + private boolean acceptsProfile; + + private List events; + + public SpringProfileAction(Environment environment) { + this.environment = environment; + } + + @Override + public void begin(InterpretationContext ic, String name, Attributes attributes) + throws ActionException { + this.depth++; + if (this.depth != 1) { + return; + } + ic.pushObject(this); + this.acceptsProfile = acceptsProfiles(ic, attributes); + this.events = new ArrayList(); + ic.addInPlayListener(this); + } + + private boolean acceptsProfiles(InterpretationContext ic, Attributes attributes) { + String profileName = attributes.getValue(NAME_ATTRIBUTE); + if (!OptionHelper.isEmpty(profileName)) { + OptionHelper.substVars(profileName, ic, this.context); + return this.environment != null + && this.environment.acceptsProfiles(profileName); + } + return false; + } + + @Override + public void end(InterpretationContext ic, String name) throws ActionException { + this.depth--; + if (this.depth != 0) { + return; + } + ic.removeInPlayListener(this); + verifyAndPop(ic); + if (this.acceptsProfile) { + addEventsToPlayer(ic); + } + } + + private void verifyAndPop(InterpretationContext ic) { + Object o = ic.peekObject(); + Assert.state(o != null, "Unexpected null object on stack"); + Assert.isInstanceOf(SpringProfileAction.class, o, "logback stack error"); + Assert.state(o == this, "ProfileAction different than current one on stack"); + ic.popObject(); + } + + private void addEventsToPlayer(InterpretationContext ic) { + Interpreter interpreter = ic.getJoranInterpreter(); + this.events.remove(0); + this.events.remove(this.events.size() - 1); + interpreter.getEventPlayer().addEventsDynamically(this.events, 1); + } + + @Override + public void inPlay(SaxEvent event) { + this.events.add(event); + } + +} diff --git a/spring-boot/src/test/java/org/springframework/boot/logging/java/JavaLoggerSystemTests.java b/spring-boot/src/test/java/org/springframework/boot/logging/java/JavaLoggerSystemTests.java index 280cc13b2fa..25923ac5bd9 100644 --- a/spring-boot/src/test/java/org/springframework/boot/logging/java/JavaLoggerSystemTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/logging/java/JavaLoggerSystemTests.java @@ -81,7 +81,7 @@ public class JavaLoggerSystemTests extends AbstractLoggingSystemTests { public void noFile() throws Exception { this.loggingSystem.beforeInitialize(); this.logger.info("Hidden"); - this.loggingSystem.initialize(null, null); + this.loggingSystem.initialize(null, null, null); this.logger.info("Hello world"); String output = this.output.toString().trim(); assertTrue("Wrong output:\n" + output, output.contains("Hello world")); @@ -98,7 +98,7 @@ public class JavaLoggerSystemTests extends AbstractLoggingSystemTests { } this.loggingSystem.beforeInitialize(); this.logger.info("Hidden"); - this.loggingSystem.initialize(null, getLogFile(null, tmpDir())); + this.loggingSystem.initialize(null, null, getLogFile(null, tmpDir())); this.logger.info("Hello world"); String output = this.output.toString().trim(); assertTrue("Wrong output:\n" + output, output.contains("Hello world")); @@ -109,7 +109,7 @@ public class JavaLoggerSystemTests extends AbstractLoggingSystemTests { @Test public void testCustomFormatter() throws Exception { this.loggingSystem.beforeInitialize(); - this.loggingSystem.initialize(null, null); + this.loggingSystem.initialize(null, null, null); this.logger.info("Hello world"); String output = this.output.toString().trim(); assertTrue("Wrong output:\n" + output, output.contains("Hello world")); @@ -121,6 +121,7 @@ public class JavaLoggerSystemTests extends AbstractLoggingSystemTests { System.setProperty("PID", "1234"); this.loggingSystem.beforeInitialize(); this.loggingSystem.initialize( + null, "classpath:" + ClassUtils.addResourcePathToPackagePath(getClass(), "logging.properties"), null); @@ -134,7 +135,8 @@ public class JavaLoggerSystemTests extends AbstractLoggingSystemTests { @Test public void testNonDefaultConfigLocation() throws Exception { this.loggingSystem.beforeInitialize(); - this.loggingSystem.initialize("classpath:logging-nondefault.properties", null); + this.loggingSystem.initialize(null, "classpath:logging-nondefault.properties", + null); this.logger.info("Hello world"); String output = this.output.toString().trim(); assertTrue("Wrong output:\n" + output, output.contains("INFO: Hello")); @@ -143,13 +145,14 @@ public class JavaLoggerSystemTests extends AbstractLoggingSystemTests { @Test(expected = IllegalStateException.class) public void testNonexistentConfigLocation() throws Exception { this.loggingSystem.beforeInitialize(); - this.loggingSystem.initialize("classpath:logging-nonexistent.properties", null); + this.loggingSystem.initialize(null, "classpath:logging-nonexistent.properties", + null); } @Test public void setLevel() throws Exception { this.loggingSystem.beforeInitialize(); - this.loggingSystem.initialize(null, null); + this.loggingSystem.initialize(null, null, null); this.logger.debug("Hello"); this.loggingSystem.setLogLevel("org.springframework.boot", LogLevel.DEBUG); this.logger.debug("Hello"); diff --git a/spring-boot/src/test/java/org/springframework/boot/logging/log4j/Log4JLoggingSystemTests.java b/spring-boot/src/test/java/org/springframework/boot/logging/log4j/Log4JLoggingSystemTests.java index af39dca37dd..e0ad064f8d6 100644 --- a/spring-boot/src/test/java/org/springframework/boot/logging/log4j/Log4JLoggingSystemTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/logging/log4j/Log4JLoggingSystemTests.java @@ -68,7 +68,7 @@ public class Log4JLoggingSystemTests extends AbstractLoggingSystemTests { public void noFile() throws Exception { this.loggingSystem.beforeInitialize(); this.logger.info("Hidden"); - this.loggingSystem.initialize(null, null); + this.loggingSystem.initialize(null, null, null); this.logger.info("Hello world"); String output = this.output.toString().trim(); assertTrue("Wrong output:\n" + output, output.contains("Hello world")); @@ -80,7 +80,7 @@ public class Log4JLoggingSystemTests extends AbstractLoggingSystemTests { public void withFile() throws Exception { this.loggingSystem.beforeInitialize(); this.logger.info("Hidden"); - this.loggingSystem.initialize(null, getLogFile(null, tmpDir())); + this.loggingSystem.initialize(null, null, getLogFile(null, tmpDir())); this.logger.info("Hello world"); String output = this.output.toString().trim(); assertTrue("Wrong output:\n" + output, output.contains("Hello world")); @@ -91,7 +91,7 @@ public class Log4JLoggingSystemTests extends AbstractLoggingSystemTests { @Test public void testNonDefaultConfigLocation() throws Exception { this.loggingSystem.beforeInitialize(); - this.loggingSystem.initialize("classpath:log4j-nondefault.properties", + this.loggingSystem.initialize(null, "classpath:log4j-nondefault.properties", getLogFile(null, tmpDir())); this.logger.info("Hello world"); String output = this.output.toString().trim(); @@ -103,13 +103,13 @@ public class Log4JLoggingSystemTests extends AbstractLoggingSystemTests { @Test(expected = IllegalStateException.class) public void testNonexistentConfigLocation() throws Exception { this.loggingSystem.beforeInitialize(); - this.loggingSystem.initialize("classpath:log4j-nonexistent.xml", null); + this.loggingSystem.initialize(null, "classpath:log4j-nonexistent.xml", null); } @Test public void setLevel() throws Exception { this.loggingSystem.beforeInitialize(); - this.loggingSystem.initialize(null, null); + this.loggingSystem.initialize(null, null, null); this.logger.debug("Hello"); this.loggingSystem.setLogLevel("org.springframework.boot", LogLevel.DEBUG); this.logger.debug("Hello"); @@ -121,7 +121,7 @@ public class Log4JLoggingSystemTests extends AbstractLoggingSystemTests { @Ignore("Fails on Bamboo") public void loggingThatUsesJulIsCaptured() { this.loggingSystem.beforeInitialize(); - this.loggingSystem.initialize(null, null); + this.loggingSystem.initialize(null, null, null); java.util.logging.Logger julLogger = java.util.logging.Logger .getLogger(getClass().getName()); julLogger.severe("Hello world"); diff --git a/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystemTests.java b/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystemTests.java index 1b51e70ad28..025031331aa 100644 --- a/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystemTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystemTests.java @@ -71,7 +71,7 @@ public class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests { public void noFile() throws Exception { this.loggingSystem.beforeInitialize(); this.logger.info("Hidden"); - this.loggingSystem.initialize(null, null); + this.loggingSystem.initialize(null, null, null); this.logger.info("Hello world"); String output = this.output.toString().trim(); assertTrue("Wrong output:\n" + output, output.contains("Hello world")); @@ -85,7 +85,7 @@ public class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests { public void withFile() throws Exception { this.loggingSystem.beforeInitialize(); this.logger.info("Hidden"); - this.loggingSystem.initialize(null, getLogFile(null, tmpDir())); + this.loggingSystem.initialize(null, null, getLogFile(null, tmpDir())); this.logger.info("Hello world"); String output = this.output.toString().trim(); assertTrue("Wrong output:\n" + output, output.contains("Hello world")); @@ -98,7 +98,7 @@ public class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests { @Test public void testNonDefaultConfigLocation() throws Exception { this.loggingSystem.beforeInitialize(); - this.loggingSystem.initialize("classpath:log4j2-nondefault.xml", + this.loggingSystem.initialize(null, "classpath:log4j2-nondefault.xml", getLogFile(tmpDir() + "/tmp.log", null)); this.logger.info("Hello world"); String output = this.output.toString().trim(); @@ -116,13 +116,13 @@ public class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests { @Test(expected = IllegalStateException.class) public void testNonexistentConfigLocation() throws Exception { this.loggingSystem.beforeInitialize(); - this.loggingSystem.initialize("classpath:log4j2-nonexistent.xml", null); + this.loggingSystem.initialize(null, "classpath:log4j2-nonexistent.xml", null); } @Test public void setLevel() throws Exception { this.loggingSystem.beforeInitialize(); - this.loggingSystem.initialize(null, null); + this.loggingSystem.initialize(null, null, null); this.logger.debug("Hello"); this.loggingSystem.setLogLevel("org.springframework.boot", LogLevel.DEBUG); this.logger.debug("Hello"); @@ -134,7 +134,7 @@ public class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests { @Ignore("Fails on Bamboo") public void loggingThatUsesJulIsCaptured() { this.loggingSystem.beforeInitialize(); - this.loggingSystem.initialize(null, null); + this.loggingSystem.initialize(null, null, null); java.util.logging.Logger julLogger = java.util.logging.Logger .getLogger(getClass().getName()); julLogger.severe("Hello world"); diff --git a/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemTests.java b/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemTests.java index 37f0344b7fe..6fe9c1fe863 100644 --- a/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemTests.java @@ -31,7 +31,9 @@ import org.slf4j.bridge.SLF4JBridgeHandler; import org.slf4j.impl.StaticLoggerBinder; import org.springframework.boot.logging.AbstractLoggingSystemTests; import org.springframework.boot.logging.LogLevel; +import org.springframework.boot.logging.LoggingInitializationContext; import org.springframework.boot.test.OutputCapture; +import org.springframework.mock.env.MockEnvironment; import org.springframework.util.StringUtils; import ch.qos.logback.classic.Logger; @@ -61,9 +63,13 @@ public class LogbackLoggingSystemTests extends AbstractLoggingSystemTests { private Log logger; + private LoggingInitializationContext initializationContext; + @Before public void setup() { this.logger = new SLF4JLogFactory().getInstance(getClass().getName()); + this.initializationContext = new LoggingInitializationContext( + new MockEnvironment()); } @Override @@ -76,7 +82,7 @@ public class LogbackLoggingSystemTests extends AbstractLoggingSystemTests { public void noFile() throws Exception { this.loggingSystem.beforeInitialize(); this.logger.info("Hidden"); - this.loggingSystem.initialize(null, null); + this.loggingSystem.initialize(this.initializationContext, null, null); this.logger.info("Hello world"); String output = this.output.toString().trim(); assertTrue("Wrong output:\n" + output, output.contains("Hello world")); @@ -88,7 +94,8 @@ public class LogbackLoggingSystemTests extends AbstractLoggingSystemTests { public void withFile() throws Exception { this.loggingSystem.beforeInitialize(); this.logger.info("Hidden"); - this.loggingSystem.initialize(null, getLogFile(null, tmpDir())); + this.loggingSystem.initialize(this.initializationContext, null, + getLogFile(null, tmpDir())); this.logger.info("Hello world"); String output = this.output.toString().trim(); assertTrue("Wrong output:\n" + output, output.contains("Hello world")); @@ -108,7 +115,8 @@ public class LogbackLoggingSystemTests extends AbstractLoggingSystemTests { @Test public void testNonDefaultConfigLocation() throws Exception { this.loggingSystem.beforeInitialize(); - this.loggingSystem.initialize("classpath:logback-nondefault.xml", + this.loggingSystem.initialize(this.initializationContext, + "classpath:logback-nondefault.xml", getLogFile(tmpDir() + "/tmp.log", null)); this.logger.info("Hello world"); String output = this.output.toString().trim(); @@ -121,13 +129,14 @@ public class LogbackLoggingSystemTests extends AbstractLoggingSystemTests { @Test(expected = IllegalStateException.class) public void testNonexistentConfigLocation() throws Exception { this.loggingSystem.beforeInitialize(); - this.loggingSystem.initialize("classpath:logback-nonexistent.xml", null); + this.loggingSystem.initialize(this.initializationContext, + "classpath:logback-nonexistent.xml", null); } @Test public void setLevel() throws Exception { this.loggingSystem.beforeInitialize(); - this.loggingSystem.initialize(null, null); + this.loggingSystem.initialize(this.initializationContext, null, null); this.logger.debug("Hello"); this.loggingSystem.setLogLevel("org.springframework.boot", LogLevel.DEBUG); this.logger.debug("Hello"); @@ -138,7 +147,7 @@ public class LogbackLoggingSystemTests extends AbstractLoggingSystemTests { @Test public void loggingThatUsesJulIsCaptured() { this.loggingSystem.beforeInitialize(); - this.loggingSystem.initialize(null, null); + this.loggingSystem.initialize(this.initializationContext, null, null); java.util.logging.Logger julLogger = java.util.logging.Logger .getLogger(getClass().getName()); julLogger.info("Hello world"); diff --git a/spring-boot/src/test/java/org/springframework/boot/logging/logback/SpringBootJoranConfiguratorTests.java b/spring-boot/src/test/java/org/springframework/boot/logging/logback/SpringBootJoranConfiguratorTests.java new file mode 100644 index 00000000000..25869f44e93 --- /dev/null +++ b/spring-boot/src/test/java/org/springframework/boot/logging/logback/SpringBootJoranConfiguratorTests.java @@ -0,0 +1,125 @@ +/* + * Copyright 2012-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.logging.logback; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.impl.StaticLoggerBinder; +import org.springframework.boot.logging.LoggingInitializationContext; +import org.springframework.boot.test.OutputCapture; +import org.springframework.mock.env.MockEnvironment; + +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.joran.JoranConfigurator; +import ch.qos.logback.core.joran.spi.JoranException; + +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.not; + +/** + * Tests for {@link SpringBootJoranConfigurator}. + * + * @author Phillip Webb + */ +public class SpringBootJoranConfiguratorTests { + + @Rule + public OutputCapture out = new OutputCapture(); + + private MockEnvironment environment; + + private LoggingInitializationContext initializationContext; + + private JoranConfigurator configurator; + + private LoggerContext context; + + private Logger logger; + + @Before + public void setup() { + this.environment = new MockEnvironment(); + this.initializationContext = new LoggingInitializationContext(this.environment); + this.configurator = new SpringBootJoranConfigurator(this.initializationContext); + StaticLoggerBinder binder = StaticLoggerBinder.getSingleton(); + this.context = (LoggerContext) binder.getLoggerFactory(); + this.logger = this.context.getLogger(getClass()); + } + + @After + public void reset() { + this.context.reset(); + } + + @Test + public void profileActive() throws Exception { + this.environment.setActiveProfiles("production"); + initialize("production-profile.xml"); + this.logger.trace("Hello"); + this.out.expect(containsString("Hello")); + } + + @Test + public void profileNotActive() throws Exception { + initialize("production-profile.xml"); + this.logger.trace("Hello"); + this.out.expect(not(containsString("Hello"))); + } + + @Test + public void profileNestedActiveActive() throws Exception { + doTestNestedProfile(true, "outer", "inner"); + } + + @Test + public void profileNestedActiveNotActive() throws Exception { + doTestNestedProfile(false, "outer"); + } + + @Test + public void profileNestedNotActiveActive() throws Exception { + doTestNestedProfile(false, "inner"); + } + + @Test + public void profileNestedNotActiveNotActive() throws Exception { + doTestNestedProfile(false); + } + + private void doTestNestedProfile(boolean expected, String... profiles) + throws JoranException { + this.environment.setActiveProfiles(profiles); + initialize("nested.xml"); + this.logger.trace("Hello"); + if (expected) { + this.out.expect(containsString("Hello")); + } + else { + this.out.expect(not(containsString("Hello"))); + } + + } + + private void initialize(String config) throws JoranException { + this.configurator.setContext(this.context); + this.configurator.doConfigure(getClass().getResourceAsStream(config)); + } + +} diff --git a/spring-boot/src/test/resources/org/springframework/boot/logging/logback/nested.xml b/spring-boot/src/test/resources/org/springframework/boot/logging/logback/nested.xml new file mode 100644 index 00000000000..9ad8b045680 --- /dev/null +++ b/spring-boot/src/test/resources/org/springframework/boot/logging/logback/nested.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/spring-boot/src/test/resources/org/springframework/boot/logging/logback/production-profile.xml b/spring-boot/src/test/resources/org/springframework/boot/logging/logback/production-profile.xml new file mode 100644 index 00000000000..8b263afd5dd --- /dev/null +++ b/spring-boot/src/test/resources/org/springframework/boot/logging/logback/production-profile.xml @@ -0,0 +1,7 @@ + + + + + + +