From 055ace37f006120b0006956b03c7f358d5f3729f Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Fri, 26 Jun 2015 13:44:07 -0700 Subject: [PATCH] Support in logback configurations Include support for a new element which can be used in `logback-spring.xml` files to add properties from the Spring Environment. For example: ... ${destination} ... ... Fixes gh-1788 --- .../logback/SpringBootJoranConfigurator.java | 4 + .../logging/logback/SpringPropertyAction.java | 81 +++++++++++++++++++ .../SpringBootJoranConfiguratorTests.java | 17 ++++ .../boot/logging/logback/property.xml | 5 ++ 4 files changed, 107 insertions(+) create mode 100644 spring-boot/src/main/java/org/springframework/boot/logging/logback/SpringPropertyAction.java create mode 100644 spring-boot/src/test/resources/org/springframework/boot/logging/logback/property.xml 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 index 6ef15c5b8c..6596d67477 100644 --- 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 @@ -17,6 +17,7 @@ package org.springframework.boot.logging.logback; import org.springframework.boot.logging.LoggingInitializationContext; +import org.springframework.core.env.Environment; import ch.qos.logback.classic.joran.JoranConfigurator; import ch.qos.logback.core.joran.action.NOPAction; @@ -40,6 +41,9 @@ class SpringBootJoranConfigurator extends JoranConfigurator { @Override public void addInstanceRules(RuleStore rs) { super.addInstanceRules(rs); + Environment environment = this.initializationContext.getEnvironment(); + rs.addRule(new ElementSelector("configuration/springProperty"), + new SpringPropertyAction(environment)); 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/SpringPropertyAction.java b/spring-boot/src/main/java/org/springframework/boot/logging/logback/SpringPropertyAction.java new file mode 100644 index 0000000000..661bcf9aae --- /dev/null +++ b/spring-boot/src/main/java/org/springframework/boot/logging/logback/SpringPropertyAction.java @@ -0,0 +1,81 @@ +/* + * 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.bind.RelaxedPropertyResolver; +import org.springframework.core.env.Environment; +import org.xml.sax.Attributes; + +import ch.qos.logback.core.joran.action.Action; +import ch.qos.logback.core.joran.action.ActionUtil; +import ch.qos.logback.core.joran.action.ActionUtil.Scope; +import ch.qos.logback.core.joran.spi.ActionException; +import ch.qos.logback.core.joran.spi.InterpretationContext; +import ch.qos.logback.core.util.OptionHelper; + +/** + * Lockback {@link Action} to support {@code } tags. Allows logback + * properties to be sourced from the Spring environment. + * + * @author Phillip Webb + */ +class SpringPropertyAction extends Action { + + private static final String SOURCE_ATTRIBUTE = "source"; + + private final Environment environment; + + public SpringPropertyAction(Environment environment) { + this.environment = environment; + } + + @Override + public void begin(InterpretationContext ic, String elementName, Attributes attributes) + throws ActionException { + String name = attributes.getValue(NAME_ATTRIBUTE); + String source = attributes.getValue(SOURCE_ATTRIBUTE); + Scope scope = ActionUtil.stringToScope(attributes.getValue(SCOPE_ATTRIBUTE)); + if (OptionHelper.isEmpty(name) || OptionHelper.isEmpty(source)) { + addError("The \"name\" and \"source\" attributes of must be set"); + } + ActionUtil.setProperty(ic, name, getValue(source), scope); + } + + private String getValue(String source) { + if (this.environment == null) { + addWarn("No Spring Environment available to resolve " + source); + return null; + } + String value = this.environment.getProperty(source); + if (value != null) { + return value; + } + int lastDot = source.lastIndexOf("."); + if (lastDot > 0) { + String prefix = source.substring(0, lastDot + 1); + RelaxedPropertyResolver resolver = new RelaxedPropertyResolver( + this.environment, prefix); + return resolver.getProperty(source.substring(lastDot + 1)); + } + return null; + } + + @Override + public void end(InterpretationContext ic, String name) throws ActionException { + } + +} 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 index 25869f44e9..fc87e542d1 100644 --- 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 @@ -23,6 +23,7 @@ import org.junit.Test; import org.slf4j.Logger; import org.slf4j.impl.StaticLoggerBinder; import org.springframework.boot.logging.LoggingInitializationContext; +import org.springframework.boot.test.EnvironmentTestUtils; import org.springframework.boot.test.OutputCapture; import org.springframework.mock.env.MockEnvironment; @@ -31,7 +32,9 @@ 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.equalTo; import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertThat; /** * Tests for {@link SpringBootJoranConfigurator}. @@ -103,6 +106,20 @@ public class SpringBootJoranConfiguratorTests { doTestNestedProfile(false); } + @Test + public void springProperty() throws Exception { + EnvironmentTestUtils.addEnvironment(this.environment, "my.example-property:test"); + initialize("property.xml"); + assertThat(this.context.getProperty("MINE"), equalTo("test")); + } + + @Test + public void relaxedSpringProperty() throws Exception { + EnvironmentTestUtils.addEnvironment(this.environment, "my.EXAMPLE_PROPERTY:test"); + initialize("property.xml"); + assertThat(this.context.getProperty("MINE"), equalTo("test")); + } + private void doTestNestedProfile(boolean expected, String... profiles) throws JoranException { this.environment.setActiveProfiles(profiles); diff --git a/spring-boot/src/test/resources/org/springframework/boot/logging/logback/property.xml b/spring-boot/src/test/resources/org/springframework/boot/logging/logback/property.xml new file mode 100644 index 0000000000..5b9e062b32 --- /dev/null +++ b/spring-boot/src/test/resources/org/springframework/boot/logging/logback/property.xml @@ -0,0 +1,5 @@ + + + + +