Log useful information on startup

Include the bootstrap version number in the banner and log information
such as the host name when starting.

Issue: #53030523
This commit is contained in:
Phillip Webb 2013-07-12 00:28:48 -07:00
parent 7d824a5701
commit d4b9014b64
5 changed files with 256 additions and 2 deletions

View File

@ -41,7 +41,9 @@ abstract class Banner {
for (String line : BANNER) {
printStream.println(line);
}
printStream.println();
String version = Banner.class.getPackage().getImplementationVersion();
printStream.println(" Spring Bootstrap"
+ (version == null ? "" : " (v" + version + ")"));
printStream.println();
}

View File

@ -23,6 +23,8 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
@ -129,10 +131,16 @@ public class SpringApplication {
private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" };
private final Log log = LogFactory.getLog(getClass());
private Object[] sources;
private Class<?> mainApplicationClass;
private boolean showBanner = true;
private boolean logStartupInfo = true;
private boolean addCommandLineProperties = true;
private ResourceLoader resourceLoader;
@ -193,6 +201,7 @@ public class SpringApplication {
for (ApplicationContextInitializer<?> initializer : factories) {
this.initializers.add(initializer);
}
this.mainApplicationClass = deduceMainApplicationClass();
}
private boolean deduceWebEnvironment() {
@ -204,6 +213,21 @@ public class SpringApplication {
return true;
}
private Class<?> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
/**
* Run the Spring application, creating and refreshing a new
* {@link ApplicationContext}.
@ -220,6 +244,9 @@ public class SpringApplication {
if (context instanceof ConfigurableApplicationContext) {
applyInitializers((ConfigurableApplicationContext) context);
}
if (this.logStartupInfo) {
logStartupInfo();
}
load(context, this.sources);
refresh(context);
runCommandLineRunners(context, args);
@ -251,6 +278,21 @@ public class SpringApplication {
}
}
protected void logStartupInfo() {
new StartupInfoLogger(this.mainApplicationClass).log(getApplicationLog());
}
/**
* Returns the {@link Log} for the application. By default will be deduced.
* @return the application log
*/
protected Log getApplicationLog() {
if (this.mainApplicationClass == null) {
return this.log;
}
return LogFactory.getLog(this.mainApplicationClass);
}
/**
* Strategy method used to create the {@link ApplicationContext}. By default this
* method will respect any explicitly set application context or application context
@ -454,6 +496,16 @@ public class SpringApplication {
((AbstractApplicationContext) applicationContext).refresh();
}
/**
* Set a specific main application class that will be used as a log source and to
* obtain version information. By default the main application class will be deduced.
* Can be set to {@code null} if there is no explicit application class.
* @param mainApplicationClass the mainApplicationClass to set or {@code null}
*/
public void setMainApplicationClass(Class<?> mainApplicationClass) {
this.mainApplicationClass = mainApplicationClass;
}
/**
* Sets if this application is running within a web environment. If not specified will
* attempt to deduce the environment based on the classpath.
@ -473,6 +525,15 @@ public class SpringApplication {
this.showBanner = showBanner;
}
/**
* Sets if the application information should be logged when the application starts.
* Defaults to {@code true}
* @param logStartupInfo if startup info should be logged.
*/
public void setLogStartupInfo(boolean logStartupInfo) {
this.logStartupInfo = logStartupInfo;
}
/**
* Sets if a {@link CommandLinePropertySource} should be added to the application
* context in order to expose arguments. Defaults to {@code true}.
@ -590,6 +651,21 @@ public class SpringApplication {
return new SpringApplication(sources).run(args);
}
/**
* Static helper that can be used to run a {@link SpringApplication} from a script
* using the specified sources with default settings. This method is useful when
* calling this calls from a script environment that will not have a single main
* application class.
* @param sources the sources to load
* @param args the application arguments (usually passed from a Java main method)
* @return the running {@link ApplicationContext}
*/
public static ApplicationContext runFromScript(Object[] sources, String[] args) {
SpringApplication application = new SpringApplication(sources);
application.setMainApplicationClass(null);
return application.run(args);
}
/**
* Static helper that can be used to exit a {@link SpringApplication} and obtain a
* code indicating success (0) or otherwise. Does not throw exceptions but should

View File

@ -0,0 +1,143 @@
/*
* 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;
import java.io.File;
import java.net.InetAddress;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.security.ProtectionDomain;
import java.util.concurrent.Callable;
import org.apache.commons.logging.Log;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
/**
* Logs application information on startup.
*
* @author Phillip Webb
*/
class StartupInfoLogger {
private final Class<?> sourceClass;
public StartupInfoLogger(Class<?> sourceClass) {
this.sourceClass = sourceClass;
}
public void log(Log log) {
Assert.notNull(log, "Log must not be null");
StringBuilder message = new StringBuilder();
message.append("Starting ");
message.append(getApplicationName());
message.append(getVersion());
message.append(getOn());
message.append(getPid());
message.append(getContext());
log.info(message);
}
private String getApplicationName() {
return (this.sourceClass != null ? ClassUtils.getShortName(this.sourceClass)
: "application");
}
private String getVersion() {
return getValue(" v", new Callable<Object>() {
@Override
public Object call() throws Exception {
return StartupInfoLogger.this.sourceClass.getPackage()
.getImplementationVersion();
}
});
}
private String getOn() {
return getValue(" on ", new Callable<Object>() {
@Override
public Object call() throws Exception {
return InetAddress.getLocalHost().getHostName();
}
});
}
private String getPid() {
return getValue(" with PID ", new Callable<Object>() {
@Override
public Object call() throws Exception {
return System.getProperty("PID");
}
});
}
private String getContext() {
String startedBy = getValue("started by ", new Callable<Object>() {
@Override
public Object call() throws Exception {
return System.getProperty("user.name");
}
});
File codeSourceLocation = getCodeSourceLocation();
String path = (codeSourceLocation == null ? "" : codeSourceLocation
.getAbsolutePath());
if (startedBy == null && codeSourceLocation == null) {
return "";
}
if (StringUtils.hasLength(startedBy) && StringUtils.hasLength(path)) {
startedBy = " " + startedBy;
}
return " (" + path + startedBy + ")";
}
private File getCodeSourceLocation() {
try {
ProtectionDomain protectionDomain = (this.sourceClass == null ? getClass()
: this.sourceClass).getProtectionDomain();
URL location = protectionDomain.getCodeSource().getLocation();
File file;
URLConnection connection = location.openConnection();
if (connection instanceof JarURLConnection) {
file = new File(((JarURLConnection) connection).getJarFile().getName());
}
else {
file = new File(location.getPath());
}
if (file.exists()) {
return file;
}
}
catch (Exception ex) {
}
return null;
}
private String getValue(String prefix, Callable<Object> call) {
try {
Object value = call.call();
if (value != null && StringUtils.hasLength(value.toString())) {
return prefix + value;
}
}
catch (Exception ex) {
// Swallow and continue
}
return "";
}
}

View File

@ -0,0 +1,33 @@
/*
* 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;
import org.junit.Test;
/**
* Tests for {@link Banner}.
*
* @author Phillip Webb
*/
public class BannerTests {
@Test
public void visualBannder() throws Exception {
Banner.write(System.out);
}
}

View File

@ -131,7 +131,7 @@ public class SpringApplicationRunner {
// User reflection to load and call Spring
Class<?> application = getContextClassLoader().loadClass(
"org.springframework.bootstrap.SpringApplication");
Method method = application.getMethod("run", Object[].class,
Method method = application.getMethod("runFromScript", Object[].class,
String[].class);
this.applicationContext = method.invoke(null, this.sources,
SpringApplicationRunner.this.args);