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 1a53ea0b0f..ca1aa54290 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 = "common"; + + /** + * Enable access log. + */ + private boolean accessLogEnabled = false; + + /** + * Undertow access log directory. + */ + private File accessLogDir = new File("logs"); + public Integer getBufferSize() { return this.bufferSize; } @@ -618,12 +634,39 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer, Ord this.directBuffers = directBuffers; } + public String getAccessLogPattern() { + return this.accessLogPattern; + } + + public void setAccessLogPattern(String accessLogPattern) { + this.accessLogPattern = accessLogPattern; + } + + public boolean isAccessLogEnabled() { + return this.accessLogEnabled; + } + + public void setAccessLogEnabled(boolean accessLogEnabled) { + this.accessLogEnabled = accessLogEnabled; + } + + public File getAccessLogDir() { + return this.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-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc index 7adf9e5e22..9dbee62505 100644 --- a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -95,6 +95,9 @@ content into your application; rather pick only the properties that you need. server.tomcat.max-http-header-size= # maximum size in bytes of the HTTP message header server.tomcat.max-threads = 0 # number of threads in protocol handler server.tomcat.uri-encoding = UTF-8 # character encoding to use for URL decoding + server.undertow.access-log-enabled=false # if access logging is enabled + server.undertow.access-log-pattern=common # log pattern of the access log + server.undertow.access-log-dir=logs # access logs directory # SPRING MVC ({sc-spring-boot-autoconfigure}/web/WebMvcProperties.{sc-ext}[WebMvcProperties]) spring.mvc.locale= # set fixed locale, e.g. en_UK diff --git a/spring-boot-samples/spring-boot-sample-undertow/src/main/resources/application.properties b/spring-boot-samples/spring-boot-sample-undertow/src/main/resources/application.properties new file mode 100644 index 0000000000..9d5b25a84a --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-undertow/src/main/resources/application.properties @@ -0,0 +1,3 @@ +server.undertow.access-log-enabled=true +server.undertow.access-log-dir=target/logs +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/tomcat/TomcatEmbeddedServletContainerFactory.java b/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactory.java index fbbfbaed6e..0f82e38245 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 @@ -382,7 +382,17 @@ public class TomcatEmbeddedServletContainerFactory extends return new TomcatEmbeddedServletContainer(tomcat, getPort() >= 0); } - private File createTempDir(String prefix) { + @Override + public void setResourceLoader(ResourceLoader resourceLoader) { + this.resourceLoader = resourceLoader; + } + + /** + * 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(); @@ -396,11 +406,6 @@ public class TomcatEmbeddedServletContainerFactory extends } } - @Override - public void setResourceLoader(ResourceLoader resourceLoader) { - this.resourceLoader = resourceLoader; - } - /** * Set the Tomcat base directory. If not specified a temporary directory will be used. * @param baseDirectory the tomcat base directory 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 cc578e9f0d..c35b60835f 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,11 @@ 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.AccessLogReceiver; +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 +74,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.Xnio; +import org.xnio.XnioWorker; /** * {@link EmbeddedServletContainerFactory} that can be used to create @@ -81,6 +89,7 @@ import org.xnio.SslClientAuthMode; * * @author Ivan Sopov * @author Andy Wilkinson + * @author Marcos Barbero * @since 1.2.0 * @see UndertowEmbeddedServletContainer */ @@ -105,6 +114,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 +352,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 +363,44 @@ public class UndertowEmbeddedServletContainerFactory extends return manager; } + private void configureAccessLog(DeploymentInfo deploymentInfo) { + deploymentInfo.addInitialHandlerChainWrapper(new HandlerWrapper() { + @Override + public HttpHandler wrap(HttpHandler handler) { + return createAccessLogHandler(handler); + } + }); + } + + private AccessLogHandler createAccessLogHandler(HttpHandler handler) { + try { + createAccessLogDirectoryIfNecessary(); + AccessLogReceiver accessLogReceiver = new DefaultAccessLogReceiver( + createWorker(), this.accessLogDirectory, "access_log"); + String formatString = (this.accessLogPattern != null) ? this.accessLogPattern + : "common"; + return new AccessLogHandler(handler, accessLogReceiver, formatString, + Undertow.class.getClassLoader()); + } + catch (IOException ex) { + throw new IllegalStateException("Failed to create AccessLogHandler", ex); + } + } + + private void createAccessLogDirectoryIfNecessary() { + Assert.notNull(this.accessLogDirectory, "accesslogDirectory must not be null"); + if (!this.accessLogDirectory.isDirectory() && !this.accessLogDirectory.mkdirs()) { + throw new IllegalStateException("Failed to create access log directory '" + + this.accessLogDirectory + "'"); + } + } + + private XnioWorker createWorker() throws IOException { + Xnio xnio = Xnio.getInstance(Undertow.class.getClassLoader()); + OptionMap.Builder builder = OptionMap.builder(); + return xnio.createWorker(builder.getMap()); + } + private void registerServletContainerInitializerToDriveServletContextInitializers( DeploymentInfo deployment, ServletContextInitializer... initializers) { ServletContextInitializer[] mergedInitializers = mergeInitializers(initializers); @@ -442,6 +498,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 this.accessLogEnabled; + } + /** * Undertow {@link ResourceManager} for JAR resources. */