From f966b3b1b761c54a7be07b1ea57c067cbf9ffb7c Mon Sep 17 00:00:00 2001 From: Michael Cramer Date: Thu, 6 Nov 2014 15:32:00 +0100 Subject: [PATCH] Provide Liquibase -> Commons Logging adapter Add `CommonsLoggingLiquibaseLogger` implementation to adapt Liquibase's `Logger` to Apache Commons Logging. The `LiquibaseAutoConfiguration` class has also been updated to automatically use the adapter Fixes gh-1840 --- .../liquibase/LiquibaseAutoConfiguration.java | 5 + .../LiquibaseAutoConfigurationTests.java | 16 ++ .../CommonsLoggingLiquibaseLogger.java | 131 ++++++++++++ .../CommonsLoggingLiquibaseLoggerTests.java | 186 ++++++++++++++++++ 4 files changed, 338 insertions(+) create mode 100644 spring-boot/src/main/java/org/springframework/boot/liquibase/CommonsLoggingLiquibaseLogger.java create mode 100644 spring-boot/src/test/java/org/springframework/boot/liquibase/CommonsLoggingLiquibaseLoggerTests.java diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfiguration.java index c5e9bc2f3b..0e24e73962 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfiguration.java @@ -21,6 +21,7 @@ import javax.persistence.EntityManagerFactory; import javax.sql.DataSource; import liquibase.integration.spring.SpringLiquibase; +import liquibase.servicelocator.ServiceLocator; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.AutoConfigureAfter; @@ -33,6 +34,7 @@ import org.springframework.boot.autoconfigure.data.jpa.EntityManagerFactoryDepen import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.liquibase.CommonsLoggingLiquibaseLogger; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -82,6 +84,9 @@ public class LiquibaseAutoConfiguration { + resource + " (please add changelog or check your Liquibase " + "configuration)"); } + ServiceLocator serviceLocator = ServiceLocator.getInstance(); + serviceLocator.addPackageToScan(CommonsLoggingLiquibaseLogger.class + .getPackage().getName()); } @Bean diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfigurationTests.java index b1dda906c9..b3a9a1f857 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfigurationTests.java @@ -26,12 +26,16 @@ import org.junit.rules.ExpectedException; import org.springframework.beans.factory.BeanCreationException; import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration; +import org.springframework.boot.liquibase.CommonsLoggingLiquibaseLogger; import org.springframework.boot.test.EnvironmentTestUtils; import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.test.util.ReflectionTestUtils; +import static org.hamcrest.Matchers.instanceOf; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; /** @@ -151,4 +155,16 @@ public class LiquibaseAutoConfigurationTests { PropertyPlaceholderAutoConfiguration.class); this.context.refresh(); } + + @Test + public void testLogger() throws Exception { + this.context.register(EmbeddedDataSourceConfiguration.class, + LiquibaseAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class); + this.context.refresh(); + SpringLiquibase liquibase = this.context.getBean(SpringLiquibase.class); + Object log = ReflectionTestUtils.getField(liquibase, "log"); + assertThat(log, instanceOf(CommonsLoggingLiquibaseLogger.class)); + } + } diff --git a/spring-boot/src/main/java/org/springframework/boot/liquibase/CommonsLoggingLiquibaseLogger.java b/spring-boot/src/main/java/org/springframework/boot/liquibase/CommonsLoggingLiquibaseLogger.java new file mode 100644 index 0000000000..90ca5cfc0e --- /dev/null +++ b/spring-boot/src/main/java/org/springframework/boot/liquibase/CommonsLoggingLiquibaseLogger.java @@ -0,0 +1,131 @@ +package org.springframework.boot.liquibase; + +import liquibase.configuration.LiquibaseConfiguration; +import liquibase.logging.LogLevel; +import liquibase.logging.Logger; +import liquibase.logging.core.AbstractLogger; +import liquibase.logging.core.DefaultLoggerConfiguration; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Liqubase {@link Logger} that that delegates to an Apache Commons {@link Log}. + * + * @author Michael Cramer + * @author Phillip Webb + * @since 1.2.0 + */ +public class CommonsLoggingLiquibaseLogger extends AbstractLogger { + + public static final int PRIORITY = 10; + + private Log logger; + + @Override + public void setName(String name) { + this.logger = createLogger(name); + } + + /** + * Factory method used to create the logger. + * @param name the name of the logger + * @return a {@link Log} instance + */ + protected Log createLogger(String name) { + return LogFactory.getLog(name); + } + + @Override + public void setLogLevel(String logLevel, String logFile) { + super.setLogLevel(logLevel); + } + + @Override + public void severe(String message) { + if (isEnabled(LogLevel.SEVERE)) { + this.logger.error(buildMessage(message)); + } + } + + @Override + public void severe(String message, Throwable e) { + if (isEnabled(LogLevel.SEVERE)) { + this.logger.error(buildMessage(message), e); + } + } + + @Override + public void warning(String message) { + if (isEnabled(LogLevel.WARNING)) { + this.logger.warn(buildMessage(message)); + } + } + + @Override + public void warning(String message, Throwable e) { + if (isEnabled(LogLevel.WARNING)) { + this.logger.warn(buildMessage(message), e); + } + } + + @Override + public void info(String message) { + if (isEnabled(LogLevel.INFO)) { + this.logger.info(buildMessage(message)); + } + } + + @Override + public void info(String message, Throwable e) { + if (isEnabled(LogLevel.INFO)) { + this.logger.info(buildMessage(message), e); + } + } + + @Override + public void debug(String message) { + if (isEnabled(LogLevel.DEBUG)) { + this.logger.debug(buildMessage(message)); + } + } + + @Override + public void debug(String message, Throwable e) { + if (isEnabled(LogLevel.DEBUG)) { + this.logger.debug(buildMessage(message), e); + } + } + + @Override + public int getPriority() { + return PRIORITY; + } + + private boolean isEnabled(LogLevel level) { + if (this.logger != null && getLogLevel().compareTo(level) <= 0) { + switch (level) { + case DEBUG: + return this.logger.isDebugEnabled(); + case INFO: + return this.logger.isInfoEnabled(); + case WARNING: + return this.logger.isWarnEnabled(); + case SEVERE: + return this.logger.isErrorEnabled(); + } + } + return false; + } + + @Override + public LogLevel getLogLevel() { + LogLevel logLevel = super.getLogLevel(); + if (logLevel == null) { + return toLogLevel(LiquibaseConfiguration.getInstance() + .getConfiguration(DefaultLoggerConfiguration.class).getLogLevel()); + } + return logLevel; + } + +} diff --git a/spring-boot/src/test/java/org/springframework/boot/liquibase/CommonsLoggingLiquibaseLoggerTests.java b/spring-boot/src/test/java/org/springframework/boot/liquibase/CommonsLoggingLiquibaseLoggerTests.java new file mode 100644 index 0000000000..31dfd94562 --- /dev/null +++ b/spring-boot/src/test/java/org/springframework/boot/liquibase/CommonsLoggingLiquibaseLoggerTests.java @@ -0,0 +1,186 @@ +/* + * Copyright 2012-2014 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.liquibase; + +import liquibase.logging.LogLevel; + +import org.apache.commons.logging.Log; +import org.junit.Before; +import org.junit.Test; + +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +/** + * Tests for {@link CommonsLoggingLiquibaseLogger}. + * + * @author Phillip Webb + */ +public class CommonsLoggingLiquibaseLoggerTests { + + private Log delegate = mock(Log.class); + + private CommonsLoggingLiquibaseLogger logger; + + private Throwable ex = new Exception(); + + @Before + public void setup() { + this.logger = new MockCommonsLoggingLiquibaseLogger(); + this.logger.setName("mylog"); + } + + @Test + public void debug() { + this.logger.setLogLevel(LogLevel.DEBUG); + given(this.delegate.isDebugEnabled()).willReturn(true); + this.logger.debug("debug"); + verify(this.delegate).debug("debug"); + } + + @Test + public void debugWithException() { + this.logger.setLogLevel(LogLevel.DEBUG); + given(this.delegate.isDebugEnabled()).willReturn(true); + this.logger.debug("debug", this.ex); + verify(this.delegate).debug("debug", this.ex); + } + + @Test + public void debugWithLoggerOff() { + this.logger.setLogLevel(LogLevel.DEBUG); + given(this.delegate.isDebugEnabled()).willReturn(false); + this.logger.debug("debug"); + verify(this.delegate, never()).debug("debug"); + } + + @Test + public void debugBelowLevel() { + this.logger.setLogLevel(LogLevel.INFO); + given(this.delegate.isDebugEnabled()).willReturn(true); + this.logger.debug("debug", this.ex); + verify(this.delegate, never()).debug("debug", this.ex); + } + + @Test + public void info() { + this.logger.setLogLevel(LogLevel.INFO); + given(this.delegate.isInfoEnabled()).willReturn(true); + this.logger.info("info"); + verify(this.delegate).info("info"); + } + + @Test + public void infoWithException() { + this.logger.setLogLevel(LogLevel.INFO); + given(this.delegate.isInfoEnabled()).willReturn(true); + this.logger.info("info", this.ex); + verify(this.delegate).info("info", this.ex); + } + + @Test + public void infoWithLoggerOff() { + this.logger.setLogLevel(LogLevel.INFO); + given(this.delegate.isInfoEnabled()).willReturn(false); + this.logger.info("info"); + verify(this.delegate, never()).info("info"); + } + + @Test + public void infoBelowLevel() { + this.logger.setLogLevel(LogLevel.WARNING); + given(this.delegate.isInfoEnabled()).willReturn(true); + this.logger.info("info", this.ex); + verify(this.delegate, never()).info("info", this.ex); + } + + @Test + public void warning() { + this.logger.setLogLevel(LogLevel.WARNING); + given(this.delegate.isWarnEnabled()).willReturn(true); + this.logger.warning("warning"); + verify(this.delegate).warn("warning"); + } + + @Test + public void warningWithException() { + this.logger.setLogLevel(LogLevel.WARNING); + given(this.delegate.isWarnEnabled()).willReturn(true); + this.logger.warning("warning", this.ex); + verify(this.delegate).warn("warning", this.ex); + } + + @Test + public void warningWithLoggerOff() { + this.logger.setLogLevel(LogLevel.WARNING); + given(this.delegate.isWarnEnabled()).willReturn(false); + this.logger.warning("warning"); + verify(this.delegate, never()).warn("warning"); + } + + @Test + public void warningBelowLevel() { + this.logger.setLogLevel(LogLevel.SEVERE); + given(this.delegate.isWarnEnabled()).willReturn(true); + this.logger.warning("warning", this.ex); + verify(this.delegate, never()).warn("warning", this.ex); + } + + @Test + public void severe() { + this.logger.setLogLevel(LogLevel.SEVERE); + given(this.delegate.isErrorEnabled()).willReturn(true); + this.logger.severe("severe"); + verify(this.delegate).error("severe"); + } + + @Test + public void severeWithException() { + this.logger.setLogLevel(LogLevel.SEVERE); + given(this.delegate.isErrorEnabled()).willReturn(true); + this.logger.severe("severe", this.ex); + verify(this.delegate).error("severe", this.ex); + } + + @Test + public void severeWithLoggerOff() { + this.logger.setLogLevel(LogLevel.SEVERE); + given(this.delegate.isErrorEnabled()).willReturn(false); + this.logger.severe("severe"); + verify(this.delegate, never()).error("severe"); + } + + @Test + public void severeBelowLevel() { + this.logger.setLogLevel(LogLevel.OFF); + given(this.delegate.isErrorEnabled()).willReturn(true); + this.logger.severe("severe", this.ex); + verify(this.delegate, never()).error("severe", this.ex); + } + + private class MockCommonsLoggingLiquibaseLogger extends CommonsLoggingLiquibaseLogger { + + @Override + protected Log createLogger(String name) { + return CommonsLoggingLiquibaseLoggerTests.this.delegate; + } + + } + +}