diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java index 1a53ea0b0f2..25e641ca180 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java @@ -56,6 +56,7 @@ import org.springframework.util.StringUtils; * @author Stephane Nicoll * @author Andy Wilkinson * @author Ivan Sopov + * @author Marcos Barbero */ @ConfigurationProperties(prefix = "server", ignoreUnknownFields = false) public class ServerProperties implements EmbeddedServletContainerCustomizer, Ordered { @@ -578,6 +579,21 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer, Ord private Boolean directBuffers; + /** + * Format pattern for access logs. + */ + private String accessLogPattern; + + /** + * Enable access log. + */ + private boolean accessLogEnabled = false; + + /** + * Undertow access log directory. If not specified a temporary directory will be used. + */ + private File accessLogDir; + public Integer getBufferSize() { return this.bufferSize; } @@ -618,12 +634,39 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer, Ord this.directBuffers = directBuffers; } + public String getAccessLogPattern() { + return accessLogPattern; + } + + public void setAccessLogPattern(String accessLogPattern) { + this.accessLogPattern = accessLogPattern; + } + + public boolean isAccessLogEnabled() { + return accessLogEnabled; + } + + public void setAccessLogEnabled(boolean accessLogEnabled) { + this.accessLogEnabled = accessLogEnabled; + } + + public File getAccessLogDir() { + return accessLogDir; + } + + public void setAccessLogDir(File accessLogDir) { + this.accessLogDir = accessLogDir; + } + void customizeUndertow(UndertowEmbeddedServletContainerFactory factory) { factory.setBufferSize(this.bufferSize); factory.setBuffersPerRegion(this.buffersPerRegion); factory.setIoThreads(this.ioThreads); factory.setWorkerThreads(this.workerThreads); factory.setDirectBuffers(this.directBuffers); + factory.setAccessLogDirectory(this.accessLogDir); + factory.setAccessLogPattern(this.accessLogPattern); + factory.setAccessLogEnabled(this.accessLogEnabled); } } diff --git a/spring-boot-samples/spring-boot-sample-undertow/src/test/resources/application.properties b/spring-boot-samples/spring-boot-sample-undertow/src/test/resources/application.properties new file mode 100644 index 00000000000..db6751484a8 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-undertow/src/test/resources/application.properties @@ -0,0 +1,2 @@ +server.undertow.access-log-enabled=true +server.undertow.access-log-pattern=combined \ No newline at end of file diff --git a/spring-boot/src/main/java/org/springframework/boot/context/embedded/AbstractEmbeddedServletContainerFactory.java b/spring-boot/src/main/java/org/springframework/boot/context/embedded/AbstractEmbeddedServletContainerFactory.java index ecfdfca5d13..1c228fdbd6c 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/embedded/AbstractEmbeddedServletContainerFactory.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/embedded/AbstractEmbeddedServletContainerFactory.java @@ -32,6 +32,7 @@ import org.apache.commons.logging.LogFactory; * * @author Phillip Webb * @author Dave Syer + * @author Marcos Barbero */ public abstract class AbstractEmbeddedServletContainerFactory extends AbstractConfigurableEmbeddedServletContainer implements @@ -78,6 +79,25 @@ public abstract class AbstractEmbeddedServletContainerFactory extends return file; } + /** + * Returns the absolute temp dir for given web server. + * @param prefix webserver name + * @return The temp dir for given web server. + */ + protected File createTempDir(String prefix) { + try { + File tempFolder = File.createTempFile(prefix + ".", "." + getPort()); + tempFolder.delete(); + tempFolder.mkdir(); + tempFolder.deleteOnExit(); + return tempFolder; + } + catch (IOException ex) { + throw new EmbeddedServletContainerException( + String.format("Unable to create %s tempdir", prefix), ex); + } + } + private File getExplodedWarFileDocumentRoot() { File file = getCodeSourceArchive(); if (this.logger.isDebugEnabled()) { diff --git a/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactory.java b/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactory.java index fbbfbaed6e9..7ac76190d9f 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactory.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactory.java @@ -79,6 +79,7 @@ import org.springframework.util.StringUtils; * @author Brock Mills * @author Stephane Nicoll * @author Andy Wilkinson + * @author Marcos Barbero * @see #setPort(int) * @see #setContextLifecycleListeners(Collection) * @see TomcatEmbeddedServletContainer @@ -382,20 +383,6 @@ public class TomcatEmbeddedServletContainerFactory extends return new TomcatEmbeddedServletContainer(tomcat, getPort() >= 0); } - private File createTempDir(String prefix) { - try { - File tempFolder = File.createTempFile(prefix + ".", "." + getPort()); - tempFolder.delete(); - tempFolder.mkdir(); - tempFolder.deleteOnExit(); - return tempFolder; - } - catch (IOException ex) { - throw new EmbeddedServletContainerException( - "Unable to create Tomcat tempdir", ex); - } - } - @Override public void setResourceLoader(ResourceLoader resourceLoader) { this.resourceLoader = resourceLoader; diff --git a/spring-boot/src/main/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainerFactory.java b/spring-boot/src/main/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainerFactory.java index cc578e9f0d3..1ee386f98ba 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainerFactory.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainerFactory.java @@ -19,6 +19,10 @@ package org.springframework.boot.context.embedded.undertow; import io.undertow.Undertow; import io.undertow.Undertow.Builder; import io.undertow.UndertowMessages; +import io.undertow.server.HandlerWrapper; +import io.undertow.server.HttpHandler; +import io.undertow.server.handlers.accesslog.AccessLogHandler; +import io.undertow.server.handlers.accesslog.DefaultAccessLogReceiver; import io.undertow.server.handlers.resource.ClassPathResourceManager; import io.undertow.server.handlers.resource.FileResourceManager; import io.undertow.server.handlers.resource.Resource; @@ -69,8 +73,11 @@ import org.springframework.context.ResourceLoaderAware; import org.springframework.core.io.ResourceLoader; import org.springframework.util.Assert; import org.springframework.util.ResourceUtils; +import org.xnio.OptionMap; import org.xnio.Options; import org.xnio.SslClientAuthMode; +import org.xnio.XnioWorker; +import org.xnio.Xnio; /** * {@link EmbeddedServletContainerFactory} that can be used to create @@ -81,6 +88,7 @@ import org.xnio.SslClientAuthMode; * * @author Ivan Sopov * @author Andy Wilkinson + * @author Marcos Barbero * @since 1.2.0 * @see UndertowEmbeddedServletContainer */ @@ -105,6 +113,12 @@ public class UndertowEmbeddedServletContainerFactory extends private Boolean directBuffers; + private File accessLogDirectory; + + private String accessLogPattern; + + private boolean accessLogEnabled = false; + /** * Create a new {@link UndertowEmbeddedServletContainerFactory} instance. */ @@ -337,6 +351,9 @@ public class UndertowEmbeddedServletContainerFactory extends for (UndertowDeploymentInfoCustomizer customizer : this.deploymentInfoCustomizers) { customizer.customize(deployment); } + if (isAccessLogEnabled()) { + configureAccessLog(deployment); + } DeploymentManager manager = Servlets.defaultContainer().addDeployment(deployment); manager.deploy(); SessionManager sessionManager = manager.getDeployment().getSessionManager(); @@ -345,6 +362,51 @@ public class UndertowEmbeddedServletContainerFactory extends return manager; } + private void configureAccessLog(DeploymentInfo deploymentInfo) { + deploymentInfo.addInitialHandlerChainWrapper(new HandlerWrapper() { + @Override + public HttpHandler wrap(HttpHandler handler) { + try { + String formatString = (accessLogPattern != null) ? accessLogPattern + : "common"; + DefaultAccessLogReceiver accessLogReceiver = new DefaultAccessLogReceiver( + createWorker(), getLogsDir(), "access_log"); + return new AccessLogHandler(handler, accessLogReceiver, formatString, + Undertow.class.getClassLoader()); + } + catch (IOException ex) { + throw new IllegalStateException(ex); + } + } + }); + } + + private XnioWorker createWorker() throws IOException { + Xnio xnio = Xnio.getInstance(Undertow.class.getClassLoader()); + OptionMap.Builder builder = OptionMap.builder(); + if(this.ioThreads != null && this.ioThreads > 0) { + builder.set(Options.WORKER_IO_THREADS, ioThreads); + } + if(this.workerThreads != null && this.workerThreads > 0) { + builder.set(Options.WORKER_TASK_CORE_THREADS, workerThreads); + builder.set(Options.WORKER_TASK_MAX_THREADS, workerThreads); + } + return xnio.createWorker(builder.getMap()); + } + + private File getLogsDir() { + File logsDir; + if (accessLogDirectory != null) { + logsDir = accessLogDirectory; + if (!logsDir.isDirectory() && !logsDir.mkdirs()) { + throw new IllegalStateException("Failed to create logs dir '" + logsDir + "'"); + } + } else { + logsDir = createTempDir("undertow"); + } + return logsDir; + } + private void registerServletContainerInitializerToDriveServletContextInitializers( DeploymentInfo deployment, ServletContextInitializer... initializers) { ServletContextInitializer[] mergedInitializers = mergeInitializers(initializers); @@ -442,6 +504,22 @@ public class UndertowEmbeddedServletContainerFactory extends this.directBuffers = directBuffers; } + public void setAccessLogDirectory(File accessLogDirectory) { + this.accessLogDirectory = accessLogDirectory; + } + + public void setAccessLogPattern(String accessLogPattern) { + this.accessLogPattern = accessLogPattern; + } + + public void setAccessLogEnabled(boolean accessLogEnabled) { + this.accessLogEnabled = accessLogEnabled; + } + + public boolean isAccessLogEnabled() { + return accessLogEnabled; + } + /** * Undertow {@link ResourceManager} for JAR resources. */