Create LoggingSystem class to configure logging

Add LoggingSystem class that can be used to configure various logging
systems in a consistent way. Mostly the code is migrated from the
LoggingApplicationContextInitializer.
This commit is contained in:
Phillip Webb 2013-07-12 16:40:15 -07:00
parent ae20d389c4
commit a324beadac
9 changed files with 266 additions and 165 deletions

View File

@ -22,17 +22,14 @@ import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.bootstrap.logging.JavaLoggerConfigurer;
import org.springframework.bootstrap.logging.LogbackConfigurer;
import org.springframework.context.ApplicationContext;
import org.springframework.bootstrap.SpringApplication;
import org.springframework.bootstrap.SpringApplicationInitializer;
import org.springframework.bootstrap.logging.LoggingSystem;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.Ordered;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.ClassUtils;
import org.springframework.util.Log4jConfigurer;
import org.springframework.util.ResourceUtils;
/**
@ -68,7 +65,8 @@ import org.springframework.util.ResourceUtils;
* @author Phillip Webb
*/
public class LoggingApplicationContextInitializer implements
ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
ApplicationContextInitializer<ConfigurableApplicationContext>,
SpringApplicationInitializer, Ordered {
private static final Map<String, String> ENVIRONMENT_SYSTEM_PROPERTY_MAPPING;
static {
@ -78,8 +76,16 @@ public class LoggingApplicationContextInitializer implements
ENVIRONMENT_SYSTEM_PROPERTY_MAPPING.put("PID", "PID");
}
private final Log logger = LogFactory.getLog(getClass());
private int order = Integer.MIN_VALUE + 11;
@Override
public void initialize(SpringApplication springApplication) {
LoggingSystem.get(springApplication.getClass().getClassLoader())
.beforeInitialize();
}
/**
* Initialize the logging system according to preferences expressed through the
* {@link Environment} and the classpath.
@ -102,7 +108,23 @@ public class LoggingApplicationContextInitializer implements
}
LoggingSystem system = LoggingSystem.get(applicationContext.getClassLoader());
system.init(applicationContext);
// User specified configuration
if (environment.containsProperty("logging.config")) {
String value = environment.getProperty("logging.config");
try {
ResourceUtils.getURL(value).openStream().close();
system.initialize(value);
return;
}
catch (Exception ex) {
// Swallow exception and continue
}
this.logger.warn("Logging environment value '" + value
+ "' cannot be opened and will be ignored");
}
system.initialize();
}
private String getPid() {
@ -122,113 +144,4 @@ public class LoggingApplicationContextInitializer implements
return this.order;
}
private static enum LoggingSystem {
/**
* Log4J
*/
LOG4J("org.apache.log4j.PropertyConfigurator", "log4j.xml", "log4j.properties") {
@Override
protected void doInit(ApplicationContext applicationContext,
String configLocation) throws Exception {
Log4jConfigurer.initLogging(configLocation);
}
},
/**
* Logback
*/
LOGBACK("ch.qos.logback.core.Appender", "logback.xml") {
@Override
protected void doInit(ApplicationContext applicationContext,
String configLocation) throws Exception {
LogbackConfigurer.initLogging(configLocation);
}
},
/**
* Java Util Logging
*/
JAVA(null, "logging.properties") {
@Override
protected void doInit(ApplicationContext applicationContext,
String configLocation) throws Exception {
JavaLoggerConfigurer.initLogging(configLocation);
}
};
private final Log logger = LogFactory
.getLog(LoggingApplicationContextInitializer.class);
private final String className;
private final String[] paths;
private LoggingSystem(String className, String... paths) {
this.className = className;
this.paths = paths;
}
public void init(ApplicationContext applicationContext) {
String configLocation = getConfigLocation(applicationContext);
try {
doInit(applicationContext, configLocation);
}
catch (Exception ex) {
throw new IllegalStateException("Cannot initialize logging from "
+ configLocation, ex);
}
}
protected abstract void doInit(ApplicationContext applicationContext,
String configLocation) throws Exception;
private String getConfigLocation(ApplicationContext applicationContext) {
Environment environment = applicationContext.getEnvironment();
ClassLoader classLoader = applicationContext.getClassLoader();
// User specified config
if (environment.containsProperty("logging.config")) {
String value = environment.getProperty("logging.config");
try {
ResourceUtils.getURL(value).openStream().close();
return value;
}
catch (Exception ex) {
// Swallow exception and continue
}
this.logger.warn("Logging environment value '" + value
+ "' cannot be opened and will be ignored");
}
// Common patterns
for (String path : this.paths) {
ClassPathResource resource = new ClassPathResource(path, classLoader);
if (resource.exists()) {
return "classpath:" + path;
}
}
// Fallback to the default
String defaultPath = ClassUtils.getPackageName(JavaLoggerConfigurer.class);
defaultPath = defaultPath.replace(".", "/");
defaultPath = defaultPath + "/" + this.paths[this.paths.length - 1];
return "classpath:" + defaultPath;
}
public static LoggingSystem get(ClassLoader classLoader) {
for (LoggingSystem loggingSystem : values()) {
String className = loggingSystem.className;
if (className == null || ClassUtils.isPresent(className, classLoader)) {
return loggingSystem;
}
}
return JAVA;
}
}
}

View File

@ -0,0 +1,72 @@
/*
* 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.bootstrap.logging;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.ClassUtils;
/**
* Abstract base class for {@link LoggingSystem} implementations.
*
* @author Phillip Webb
* @author Dave Syer
*/
abstract class AbstractLoggingSystem extends LoggingSystem {
private final ClassLoader classLoader;
private final String[] paths;
public AbstractLoggingSystem(ClassLoader classLoader, String... paths) {
this.classLoader = classLoader;
this.paths = paths.clone();
}
protected final ClassLoader getClassLoader() {
return this.classLoader;
}
@Override
public void beforeInitialize() {
initializeWithSensibleDefaults();
}
@Override
public void initialize() {
for (String path : this.paths) {
ClassPathResource resource = new ClassPathResource(path, this.classLoader);
if (resource.exists()) {
initialize("classpath:" + path);
return;
}
}
initializeWithSensibleDefaults();
}
protected void initializeWithSensibleDefaults() {
initialize(getPackagedConfigFile(this.paths[this.paths.length - 1]));
}
protected final String getPackagedConfigFile(String fileName) {
String defaultPath = ClassUtils.getPackageName(getClass());
defaultPath = defaultPath.replace(".", "/");
defaultPath = defaultPath + "/" + fileName;
defaultPath = "classpath:" + defaultPath;
return defaultPath;
}
}

View File

@ -16,7 +16,6 @@
package org.springframework.bootstrap.logging;
import java.io.FileNotFoundException;
import java.util.logging.LogManager;
import java.util.logging.Logger;
@ -24,29 +23,27 @@ import org.springframework.util.ResourceUtils;
import org.springframework.util.SystemPropertyUtils;
/**
* Logging initializer for {@link Logger java.util.logging}.
* {@link LoggingSystem} for {@link Logger java.util.logging}.
*
* @author Phillip Webb
* @author Dave Syer
*/
public abstract class JavaLoggerConfigurer {
class JavaLoggingSystem extends AbstractLoggingSystem {
/**
* Configure the logging system from the specified location (a properties file).
*
* @param location the location to use to configure logging
*/
public static void initLogging(String location) throws FileNotFoundException {
String resolvedLocation = SystemPropertyUtils.resolvePlaceholders(location);
public JavaLoggingSystem(ClassLoader classLoader) {
super(classLoader, "logging.properties");
}
@Override
public void initialize(String configLocation) {
String resolvedLocation = SystemPropertyUtils.resolvePlaceholders(configLocation);
try {
LogManager.getLogManager().readConfiguration(
ResourceUtils.getURL(resolvedLocation).openStream());
}
catch (Exception ex) {
if (ex instanceof FileNotFoundException) {
throw (FileNotFoundException) ex;
}
throw new IllegalArgumentException("Could not initialize logging from "
+ location, ex);
throw new IllegalStateException("Could not initialize logging from "
+ configLocation, ex);
}
}

View File

@ -0,0 +1,44 @@
/*
* 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.bootstrap.logging;
import org.springframework.util.Log4jConfigurer;
/**
* {@link LoggingSystem} for for <a href="http://logging.apache.org/log4j">log4j</a>.
*
* @author Phillip Webb
* @author Dave Syer
*/
class Log4JLoggingSystem extends AbstractLoggingSystem {
public Log4JLoggingSystem(ClassLoader classLoader) {
super(classLoader, "log4j.xml", "log4j.properties");
}
@Override
public void initialize(String configLocation) {
try {
Log4jConfigurer.initLogging(configLocation);
}
catch (Exception ex) {
throw new IllegalStateException("Could not initialize logging from "
+ configLocation, ex);
}
}
}

View File

@ -16,41 +16,43 @@
package org.springframework.bootstrap.logging;
import java.io.FileNotFoundException;
import java.net.URL;
import org.slf4j.ILoggerFactory;
import org.slf4j.impl.StaticLoggerBinder;
import org.springframework.util.Assert;
import org.springframework.util.ResourceUtils;
import org.springframework.util.SystemPropertyUtils;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.util.ContextInitializer;
import ch.qos.logback.core.joran.spi.JoranException;
/**
* Logging initializer for <a href="http://logback.qos.ch">logback</a>.
* {@link LoggingSystem} for for <a href="http://logback.qos.ch">logback</a>.
*
* @author Phillip Webb
* @author Dave Syer
*/
public abstract class LogbackConfigurer {
class LogbackLoggingSystem extends AbstractLoggingSystem {
/**
* Configure the logback system from the specified location.
*
* @param location the location to use to configure logback
*/
public static void initLogging(String location) throws FileNotFoundException {
String resolvedLocation = SystemPropertyUtils.resolvePlaceholders(location);
URL url = ResourceUtils.getURL(resolvedLocation);
LoggerContext context = (LoggerContext) StaticLoggerBinder.getSingleton()
.getLoggerFactory();
public LogbackLoggingSystem(ClassLoader classLoader) {
super(classLoader, "logback.xml");
}
@Override
public void initialize(String configLocation) {
String resolvedLocation = SystemPropertyUtils.resolvePlaceholders(configLocation);
ILoggerFactory factory = StaticLoggerBinder.getSingleton().getLoggerFactory();
Assert.isInstanceOf(ILoggerFactory.class, factory);
LoggerContext context = (LoggerContext) factory;
context.stop();
try {
URL url = ResourceUtils.getURL(resolvedLocation);
new ContextInitializer(context).configureByResource(url);
}
catch (JoranException ex) {
throw new IllegalArgumentException("Could not initialize logging from "
+ location, ex);
catch (Exception ex) {
throw new IllegalStateException("Could not initialize logging from "
+ configLocation, ex);
}
}

View File

@ -0,0 +1,63 @@
/*
* 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.bootstrap.logging;
import org.springframework.util.ClassUtils;
/**
* Common abstraction over logging systems.
*
* @author Phillip Webb
* @author Dave Syer
*/
public abstract class LoggingSystem {
/**
* Reset the logging system to be limit output. This method may be called before
* {@link #initialize()} to reduce logging noise until the systems has been full
* Initialized.
*/
public abstract void beforeInitialize();
/**
* Initialize the logging system using sensible defaults. This method should generally
* try to find system specific configuration on classpath before falling back to
* sensible defaults.
*/
public abstract void initialize();
/**
* Initialize the logging system from a logging configuration location.
* @param configLocation a log configuration location
*/
public abstract void initialize(String configLocation);
/**
* Detect and return the logging system in use.
* @return The logging system
*/
public static LoggingSystem get(ClassLoader classLoader) {
if (ClassUtils.isPresent("ch.qos.logback.core.Appender", classLoader)) {
return new LogbackLoggingSystem(classLoader);
}
if (ClassUtils.isPresent("org.apache.log4j.PropertyConfigurator", classLoader)) {
return new Log4JLoggingSystem(classLoader);
}
return new JavaLoggingSystem(classLoader);
}
}

View File

@ -17,7 +17,6 @@
package org.springframework.bootstrap.logging;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintStream;
import java.util.logging.LogManager;
@ -30,11 +29,14 @@ import org.junit.Test;
import static org.junit.Assert.assertTrue;
/**
* Tests for {@link JavaLoggerConfigurer}.
* Tests for {@link JavaLoggingSystem}.
*
* @author Dave Syer
*/
public class JavaLoggerConfigurerTests {
public class JavaLoggerSystemTests {
private JavaLoggingSystem loggingSystem = new JavaLoggingSystem(getClass()
.getClassLoader());
private PrintStream savedOutput;
@ -67,21 +69,21 @@ public class JavaLoggerConfigurerTests {
@Test
public void testDefaultConfigLocation() throws Exception {
JavaLoggerConfigurer.initLogging("classpath:logging-nondefault.properties");
this.loggingSystem.initialize("classpath:logging-nondefault.properties");
this.logger.info("Hello world");
String output = getOutput().trim();
assertTrue("Wrong output:\n" + output, output.contains("Hello world"));
assertTrue("Wrong output:\n" + output, output.contains("INFO"));
}
@Test(expected = FileNotFoundException.class)
@Test(expected = IllegalStateException.class)
public void testNonexistentConfigLocation() throws Exception {
JavaLoggerConfigurer.initLogging("classpath:logging-nonexistent.properties");
this.loggingSystem.initialize("classpath:logging-nonexistent.properties");
}
@Test(expected = IllegalArgumentException.class)
public void testNullConfigLocation() throws Exception {
JavaLoggerConfigurer.initLogging(null);
this.loggingSystem.initialize(null);
}
}

View File

@ -17,7 +17,6 @@
package org.springframework.bootstrap.logging;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.PrintStream;
import org.apache.commons.logging.Log;
@ -29,13 +28,17 @@ import org.junit.Test;
import static org.junit.Assert.assertTrue;
/**
* Tests for {@link LogbackConfigurer}.
* Tests for {@link LogbackLoggingSystem}.
*
* @author Dave Syer
*/
public class LogbackConfigurerTests {
public class LogbackLoggingSystemTests {
private LogbackLoggingSystem loggingSystem = new LogbackLoggingSystem(getClass()
.getClassLoader());
private PrintStream savedOutput;
private ByteArrayOutputStream output;
@Before
@ -59,22 +62,22 @@ public class LogbackConfigurerTests {
@Test
public void testDefaultConfigLocation() throws Exception {
LogbackConfigurer.initLogging("classpath:logback-nondefault.xml");
Log logger = LogFactory.getLog(LogbackConfigurerTests.class);
this.loggingSystem.initialize("classpath:logback-nondefault.xml");
Log logger = LogFactory.getLog(LogbackLoggingSystemTests.class);
logger.info("Hello world");
String output = getOutput().trim();
assertTrue("Wrong output:\n" + output, output.contains("Hello world"));
assertTrue("Wrong output:\n" + output, output.startsWith("/tmp/spring.log"));
}
@Test(expected = FileNotFoundException.class)
@Test(expected = IllegalStateException.class)
public void testNonexistentConfigLocation() throws Exception {
LogbackConfigurer.initLogging("classpath:logback-nonexistent.xml");
this.loggingSystem.initialize("classpath:logback-nonexistent.xml");
}
@Test(expected = IllegalArgumentException.class)
public void testNullConfigLocation() throws Exception {
LogbackConfigurer.initLogging(null);
this.loggingSystem.initialize(null);
}
}

View File

@ -26,7 +26,9 @@ 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.junit.rules.TemporaryFolder;
import org.springframework.bootstrap.context.initializer.LoggingApplicationContextInitializer;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.env.PropertySource;
@ -41,6 +43,9 @@ import static org.junit.Assert.assertTrue;
*/
public class LoggingApplicationContextInitializerTests {
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
private LoggingApplicationContextInitializer initializer = new LoggingApplicationContextInitializer();
private Log logger = new SLF4JLogFactory().getInstance(getClass());
@ -130,7 +135,7 @@ public class LoggingApplicationContextInitializerTests {
}
if ("logging.file".equals(name)) {
return "foo.log";
}
}
return null;
}
});