diff --git a/spring-boot/pom.xml b/spring-boot/pom.xml index 24487cee38b..23ceb1d5ae5 100644 --- a/spring-boot/pom.xml +++ b/spring-boot/pom.xml @@ -39,6 +39,11 @@ javax.servlet-api true + + log4j + log4j + true + org.apache.tomcat.embed tomcat-embed-core diff --git a/spring-boot/src/main/java/org/springframework/boot/logging/LogLevel.java b/spring-boot/src/main/java/org/springframework/boot/logging/LogLevel.java new file mode 100644 index 00000000000..843cf2d749d --- /dev/null +++ b/spring-boot/src/main/java/org/springframework/boot/logging/LogLevel.java @@ -0,0 +1,28 @@ +/* + * Copyright 2012-2013 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; + +/** + * Logging levels supported by a {@link LoggingSystem}. + * + * @author Phillip Webb + */ +public enum LogLevel { + + TRACE, DEBUG, INFO, WARN, ERROR, FATAL + +} 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 4fa579758f7..425b65151ae 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 @@ -61,6 +61,13 @@ public abstract class LoggingSystem { */ public abstract void initialize(String configLocation); + /** + * Sets the logging level for a given logger. + * @param loggerName the name of the logger to set + * @param level the log level + */ + public abstract void setLogLevel(String loggerName, LogLevel level); + /** * Detect and return the logging system in use. * @return The logging system 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 9bfc93cdecd..16d4b9ef2c3 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 @@ -16,11 +16,17 @@ package org.springframework.boot.logging.java; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; import java.util.logging.LogManager; import java.util.logging.Logger; import org.springframework.boot.logging.AbstractLoggingSystem; +import org.springframework.boot.logging.LogLevel; import org.springframework.boot.logging.LoggingSystem; +import org.springframework.util.Assert; import org.springframework.util.ResourceUtils; import org.springframework.util.SystemPropertyUtils; @@ -32,12 +38,25 @@ import org.springframework.util.SystemPropertyUtils; */ public class JavaLoggingSystem extends AbstractLoggingSystem { + private static final Map LEVELS; + static { + Map levels = new HashMap(); + levels.put(LogLevel.TRACE, Level.FINEST); + levels.put(LogLevel.DEBUG, Level.FINE); + levels.put(LogLevel.INFO, Level.INFO); + levels.put(LogLevel.WARN, Level.WARNING); + levels.put(LogLevel.ERROR, Level.SEVERE); + levels.put(LogLevel.FATAL, Level.SEVERE); + LEVELS = Collections.unmodifiableMap(levels); + } + public JavaLoggingSystem(ClassLoader classLoader) { super(classLoader, "logging.properties"); } @Override public void initialize(String configLocation) { + Assert.notNull(configLocation, "ConfigLocation must not be null"); String resolvedLocation = SystemPropertyUtils.resolvePlaceholders(configLocation); try { LogManager.getLogManager().readConfiguration( @@ -49,4 +68,10 @@ public class JavaLoggingSystem extends AbstractLoggingSystem { } } + @Override + public void setLogLevel(String loggerName, LogLevel level) { + Assert.notNull(level, "Level must not be null"); + Logger logger = Logger.getLogger(loggerName == null ? "" : loggerName); + logger.setLevel(LEVELS.get(level)); + } } 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 b78b314e455..da46a2b212b 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 @@ -16,9 +16,19 @@ package org.springframework.boot.logging.log4j; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.apache.log4j.Level; +import org.apache.log4j.LogManager; +import org.apache.log4j.Logger; import org.springframework.boot.logging.AbstractLoggingSystem; +import org.springframework.boot.logging.LogLevel; import org.springframework.boot.logging.LoggingSystem; +import org.springframework.util.Assert; import org.springframework.util.Log4jConfigurer; +import org.springframework.util.StringUtils; /** * {@link LoggingSystem} for for log4j. @@ -28,12 +38,25 @@ import org.springframework.util.Log4jConfigurer; */ public class Log4JLoggingSystem extends AbstractLoggingSystem { + private static final Map LEVELS; + static { + Map levels = new HashMap(); + levels.put(LogLevel.TRACE, Level.TRACE); + levels.put(LogLevel.DEBUG, Level.DEBUG); + levels.put(LogLevel.INFO, Level.INFO); + levels.put(LogLevel.WARN, Level.WARN); + levels.put(LogLevel.ERROR, Level.ERROR); + levels.put(LogLevel.FATAL, Level.ERROR); + LEVELS = Collections.unmodifiableMap(levels); + } + public Log4JLoggingSystem(ClassLoader classLoader) { super(classLoader, "log4j.xml", "log4j.properties"); } @Override public void initialize(String configLocation) { + Assert.notNull(configLocation, "ConfigLocation must not be null"); try { Log4jConfigurer.initLogging(configLocation); } @@ -43,4 +66,11 @@ public class Log4JLoggingSystem extends AbstractLoggingSystem { } } + @Override + public void setLogLevel(String loggerName, LogLevel level) { + Logger logger = (StringUtils.hasLength(loggerName) ? LogManager + .getLogger(loggerName) : LogManager.getRootLogger()); + logger.setLevel(LEVELS.get(level)); + } + } 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 9c7e83b2eba..d45448fcc91 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 @@ -17,17 +17,24 @@ package org.springframework.boot.logging.logback; import java.net.URL; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import org.slf4j.ILoggerFactory; +import org.slf4j.Logger; import org.slf4j.bridge.SLF4JBridgeHandler; import org.slf4j.impl.StaticLoggerBinder; import org.springframework.boot.logging.AbstractLoggingSystem; +import org.springframework.boot.logging.LogLevel; import org.springframework.boot.logging.LoggingSystem; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ResourceUtils; +import org.springframework.util.StringUtils; import org.springframework.util.SystemPropertyUtils; +import ch.qos.logback.classic.Level; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.util.ContextInitializer; @@ -39,6 +46,18 @@ import ch.qos.logback.classic.util.ContextInitializer; */ public class LogbackLoggingSystem extends AbstractLoggingSystem { + private static final Map LEVELS; + static { + Map levels = new HashMap(); + levels.put(LogLevel.TRACE, Level.TRACE); + levels.put(LogLevel.DEBUG, Level.DEBUG); + levels.put(LogLevel.INFO, Level.INFO); + levels.put(LogLevel.WARN, Level.WARN); + levels.put(LogLevel.ERROR, Level.ERROR); + levels.put(LogLevel.FATAL, Level.ERROR); + LEVELS = Collections.unmodifiableMap(levels); + } + public LogbackLoggingSystem(ClassLoader classLoader) { super(classLoader, "logback.xml"); } @@ -54,6 +73,7 @@ public class LogbackLoggingSystem extends AbstractLoggingSystem { @Override public void initialize(String configLocation) { + Assert.notNull(configLocation, "ConfigLocation must not be null"); String resolvedLocation = SystemPropertyUtils.resolvePlaceholders(configLocation); ILoggerFactory factory = StaticLoggerBinder.getSingleton().getLoggerFactory(); Assert.isInstanceOf(ILoggerFactory.class, factory); @@ -69,4 +89,13 @@ public class LogbackLoggingSystem extends AbstractLoggingSystem { } } + @Override + public void setLogLevel(String loggerName, LogLevel level) { + ILoggerFactory factory = StaticLoggerBinder.getSingleton().getLoggerFactory(); + Logger logger = factory + .getLogger(StringUtils.isEmpty(loggerName) ? Logger.ROOT_LOGGER_NAME + : loggerName); + ((ch.qos.logback.classic.Logger) logger).setLevel(LEVELS.get(level)); + } + } diff --git a/spring-boot/src/main/resources/org/springframework/boot/logging/java/logging.properties b/spring-boot/src/main/resources/org/springframework/boot/logging/java/logging.properties index 77e2072e34e..ae8eb52a3d5 100644 --- a/spring-boot/src/main/resources/org/springframework/boot/logging/java/logging.properties +++ b/spring-boot/src/main/resources/org/springframework/boot/logging/java/logging.properties @@ -2,10 +2,11 @@ handlers = java.util.logging.FileHandler, java.util.logging.ConsoleHandler .level = INFO # File Logging -java.util.logging.FileHandler.pattern = %t/service.log +java.util.logging.FileHandler.pattern = %t/spring.log java.util.logging.FileHandler.formatter = org.springframework.boot.logging.java.SimpleFormatter -java.util.logging.FileHandler.level = INFO +java.util.logging.FileHandler.level = ALL java.util.logging.FileHandler.limit = 10485760 java.util.logging.FileHandler.count = 10 java.util.logging.ConsoleHandler.formatter = org.springframework.boot.logging.java.SimpleFormatter +java.util.logging.ConsoleHandler.level = ALL diff --git a/spring-boot/src/main/resources/org/springframework/boot/logging/log4j/log4j.properties b/spring-boot/src/main/resources/org/springframework/boot/logging/log4j/log4j.properties index 6442a1edf7e..7102d33f941 100644 --- a/spring-boot/src/main/resources/org/springframework/boot/logging/log4j/log4j.properties +++ b/spring-boot/src/main/resources/org/springframework/boot/logging/log4j/log4j.properties @@ -1,9 +1,8 @@ log4j.rootCategory=INFO, CONSOLE, FILE PID=???? -catalina.base=/tmp -LOG_PATH=${catalina.base}/logs -LOG_FILE=${LOG_PATH}/service.log +LOG_PATH=/tmp +LOG_FILE=${LOG_PATH}/spring.log LOG_PATTERN=[%d{yyyy-MM-dd HH:mm:ss.SSS}] service%X{context} - ${PID} %5p [%t] --- %c{1}: %m%n # CONSOLE is set to be a ConsoleAppender using a PatternLayout. 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 d5a10edb7d1..100816a1591 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 @@ -19,15 +19,17 @@ package org.springframework.boot.logging.java; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.PrintStream; -import java.util.logging.LogManager; import org.apache.commons.logging.impl.Jdk14Logger; import org.junit.After; import org.junit.Before; import org.junit.Test; -import org.springframework.boot.logging.java.JavaLoggingSystem; +import org.springframework.boot.logging.LogLevel; import org.springframework.util.ClassUtils; +import org.springframework.util.StringUtils; +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; /** @@ -48,8 +50,6 @@ public class JavaLoggerSystemTests { @Before public void init() throws SecurityException, IOException { - LogManager.getLogManager().readConfiguration( - getClass().getResourceAsStream("/logging.properties")); this.logger = new Jdk14Logger(getClass().getName()); this.savedOutput = System.err; this.output = new ByteArrayOutputStream(); @@ -71,6 +71,7 @@ public class JavaLoggerSystemTests { @Test public void testCustomFormatter() throws Exception { + this.loggingSystem.initialize(); this.logger.info("Hello world"); String output = getOutput().trim(); assertTrue("Wrong output:\n" + output, output.contains("Hello world")); @@ -108,4 +109,14 @@ public class JavaLoggerSystemTests { this.loggingSystem.initialize(null); } + @Test + public void setLevel() throws Exception { + this.loggingSystem.initialize(); + this.logger.debug("Hello"); + this.loggingSystem.setLogLevel("org.springframework.boot", LogLevel.DEBUG); + this.logger.debug("Hello"); + assertThat(StringUtils.countOccurrencesOf(this.output.toString(), "Hello"), + equalTo(1)); + } + } 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 new file mode 100644 index 00000000000..b39771b607f --- /dev/null +++ b/spring-boot/src/test/java/org/springframework/boot/logging/log4j/Log4JLoggingSystemTests.java @@ -0,0 +1,88 @@ +/* + * Copyright 2012-2013 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.log4j; + +import org.apache.commons.logging.impl.Log4JLogger; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.springframework.boot.OutputCapture; +import org.springframework.boot.logging.LogLevel; +import org.springframework.util.StringUtils; + +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +/** + * Tests for {@link Log4JLoggingSystem}. + * + * @author Phillip Webb + */ +public class Log4JLoggingSystemTests { + + @Rule + public OutputCapture output = new OutputCapture(); + + private Log4JLoggingSystem loggingSystem = new Log4JLoggingSystem(getClass() + .getClassLoader()); + + private Log4JLogger logger; + + @Before + public void setup() { + this.logger = new Log4JLogger(getClass().getName()); + } + + @After + public void clear() { + System.clearProperty("LOG_FILE"); + System.clearProperty("LOG_PATH"); + System.clearProperty("PID"); + } + + @Test + public void testNonDefaultConfigLocation() throws Exception { + this.loggingSystem.initialize("classpath:log4j-nondefault.properties"); + this.logger.info("Hello world"); + String output = this.output.toString().trim(); + assertTrue("Wrong output:\n" + output, output.contains("Hello world")); + assertTrue("Wrong output:\n" + output, output.contains("/tmp/spring.log")); + } + + @Test(expected = IllegalStateException.class) + public void testNonexistentConfigLocation() throws Exception { + this.loggingSystem.initialize("classpath:log4j-nonexistent.xml"); + } + + @Test(expected = IllegalArgumentException.class) + public void testNullConfigLocation() throws Exception { + this.loggingSystem.initialize(null); + } + + @Test + public void setLevel() throws Exception { + this.loggingSystem.initialize(); + this.logger.debug("Hello"); + this.loggingSystem.setLogLevel("org.springframework.boot", LogLevel.DEBUG); + this.logger.debug("Hello"); + assertThat(StringUtils.countOccurrencesOf(this.output.toString(), "Hello"), + equalTo(1)); + } + +} 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 051555aaf83..812567cf67d 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 @@ -17,12 +17,17 @@ package org.springframework.boot.logging.logback; import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.apache.commons.logging.impl.SLF4JLogFactory; import org.junit.After; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.springframework.boot.OutputCapture; +import org.springframework.boot.logging.LogLevel; +import org.springframework.util.StringUtils; +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; /** @@ -33,11 +38,18 @@ import static org.junit.Assert.assertTrue; public class LogbackLoggingSystemTests { @Rule - public OutputCapture outputCapture = new OutputCapture(); + public OutputCapture output = new OutputCapture(); private LogbackLoggingSystem loggingSystem = new LogbackLoggingSystem(getClass() .getClassLoader()); + private Log logger; + + @Before + public void setup() { + this.logger = new SLF4JLogFactory().getInstance(getClass().getName()); + } + @After public void clear() { System.clearProperty("LOG_FILE"); @@ -46,13 +58,12 @@ public class LogbackLoggingSystemTests { } @Test - public void testDefaultConfigLocation() throws Exception { + public void testNonDefaultConfigLocation() throws Exception { this.loggingSystem.initialize("classpath:logback-nondefault.xml"); - Log logger = LogFactory.getLog(LogbackLoggingSystemTests.class); - logger.info("Hello world"); - String output = this.outputCapture.toString().trim(); + this.logger.info("Hello world"); + String output = this.output.toString().trim(); assertTrue("Wrong output:\n" + output, output.contains("Hello world")); - assertTrue("Wrong output:\n" + output, output.startsWith("/tmp/spring.log")); + assertTrue("Wrong output:\n" + output, output.contains("/tmp/spring.log")); } @Test(expected = IllegalStateException.class) @@ -65,4 +76,14 @@ public class LogbackLoggingSystemTests { this.loggingSystem.initialize(null); } + @Test + public void setLevel() throws Exception { + this.loggingSystem.initialize(); + this.logger.debug("Hello"); + this.loggingSystem.setLogLevel("org.springframework.boot", LogLevel.DEBUG); + this.logger.debug("Hello"); + assertThat(StringUtils.countOccurrencesOf(this.output.toString(), "Hello"), + equalTo(1)); + } + } diff --git a/spring-boot/src/test/resources/log4j-nondefault.properties b/spring-boot/src/test/resources/log4j-nondefault.properties new file mode 100644 index 00000000000..544139af915 --- /dev/null +++ b/spring-boot/src/test/resources/log4j-nondefault.properties @@ -0,0 +1,12 @@ +log4j.reset=true +log4j.rootCategory=INFO, CONSOLE + +PID=???? +LOG_PATH=/tmp +LOG_FILE=${LOG_PATH}/spring.log +LOG_PATTERN=${LOG_FILE} %d{yyyy-MM-dd HH:mm:ss.SSS}] service%X{context} - ${PID} %5p [%t] --- %c{1}: %m%n + +# CONSOLE is set to be a ConsoleAppender using a PatternLayout. +log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender +log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout +log4j.appender.CONSOLE.layout.ConversionPattern=${LOG_PATTERN} diff --git a/spring-boot/src/test/resources/logging.properties b/spring-boot/src/test/resources/logging.properties deleted file mode 100644 index 187c49109c0..00000000000 --- a/spring-boot/src/test/resources/logging.properties +++ /dev/null @@ -1,4 +0,0 @@ -handlers= java.util.logging.ConsoleHandler -.level= INFO -java.util.logging.ConsoleHandler.level = INFO -java.util.logging.ConsoleHandler.formatter = org.springframework.boot.logging.java.SimpleFormatter