Support programmatic setting of log levels

Provide a common way to programmatically set log levels regardless of
the underlying log implementation.

Issue: #55202588
This commit is contained in:
Phillip Webb 2013-08-13 15:25:29 -07:00 committed by Phillip Webb
parent 5446a20a90
commit 5b77028f04
13 changed files with 272 additions and 20 deletions

View File

@ -39,6 +39,11 @@
<artifactId>javax.servlet-api</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>

View File

@ -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
}

View File

@ -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

View File

@ -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<LogLevel, Level> LEVELS;
static {
Map<LogLevel, Level> levels = new HashMap<LogLevel, Level>();
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));
}
}

View File

@ -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 <a href="http://logging.apache.org/log4j">log4j</a>.
@ -28,12 +38,25 @@ import org.springframework.util.Log4jConfigurer;
*/
public class Log4JLoggingSystem extends AbstractLoggingSystem {
private static final Map<LogLevel, Level> LEVELS;
static {
Map<LogLevel, Level> levels = new HashMap<LogLevel, Level>();
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));
}
}

View File

@ -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<LogLevel, Level> LEVELS;
static {
Map<LogLevel, Level> levels = new HashMap<LogLevel, Level>();
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));
}
}

View File

@ -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

View File

@ -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.

View File

@ -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));
}
}

View File

@ -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));
}
}

View File

@ -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));
}
}

View File

@ -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}

View File

@ -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