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 ea548d05d6e..da5b7dc9771 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 @@ -26,6 +26,16 @@ import java.util.List; import java.util.Map; import java.util.Set; +import org.eclipse.jetty.server.AbstractConnector; +import org.eclipse.jetty.server.ConnectionFactory; +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.NCSARequestLog; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.server.handler.HandlerCollection; +import org.eclipse.jetty.server.handler.HandlerWrapper; + import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.DeprecatedConfigurationProperty; import org.springframework.boot.context.properties.NestedConfigurationProperty; @@ -900,6 +910,8 @@ public class ServerProperties { */ private Integer selectors; + private final Accesslog accesslog = new Accesslog(); + public int getMaxHttpPostSize() { return this.maxHttpPostSize; } @@ -924,6 +936,157 @@ public class ServerProperties { this.selectors = selectors; } + public Accesslog getAccesslog() { + return this.accesslog; + } + + public static class Accesslog { + + /** + * Enable access log. + */ + private boolean enabled; + + /** + * accesslog filename. If no filename, logs will be redirected to System.err + */ + private String filename; + + /** + * number of days before rotated log files are deleted. Default 31 + */ + private int retainDays = 31; + + /** + * append to log. + */ + private boolean append; + + /** + * the log file name date format. + */ + private String filenameDateFormat; + + /** + * extended NCSA format. + */ + private boolean extended; + + /** + * Set the timezone of the request log. + */ + private String logTimeZone; + + /** + * Controls logging of the request cookies. + */ + private boolean logCookies; + + /** + * Controls logging of the request hostname. + */ + private boolean logServer; + + /** + * Controls logging of request processing time. + */ + private boolean logLatency; + + /** + * Set the timestamp format for request log entries in the file. If this is not set, the pre-formated request + * timestamp is used. + */ + private String logDateFormat; + + public boolean isEnabled() { + return this.enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public String getFilename() { + return this.filename; + } + + public void setFilename(String filename) { + this.filename = filename; + } + + public int getRetainDays() { + return this.retainDays; + } + + public void setRetainDays(int retainDays) { + this.retainDays = retainDays; + } + + public boolean isAppend() { + return this.append; + } + + public void setAppend(boolean append) { + this.append = append; + } + + public String getFilenameDateFormat() { + return this.filenameDateFormat; + } + + public void setFilenameDateFormat(String filenameDateFormat) { + this.filenameDateFormat = filenameDateFormat; + } + + public boolean isExtended() { + return this.extended; + } + + public void setExtended(boolean extended) { + this.extended = extended; + } + + public String getLogTimeZone() { + return this.logTimeZone; + } + + public void setLogTimeZone(String logTimeZone) { + this.logTimeZone = logTimeZone; + } + + public boolean isLogCookies() { + return this.logCookies; + } + + public void setLogCookies(boolean logCookies) { + this.logCookies = logCookies; + } + + public boolean isLogServer() { + return logServer; + } + + public void setLogServer( boolean logServer ) { + this.logServer = logServer; + } + + public boolean isLogLatency() { + return logLatency; + } + + public void setLogLatency( boolean logLatency ) { + this.logLatency = logLatency; + } + + public String getLogDateFormat() { + return logDateFormat; + } + + public void setLogDateFormat( String logDateFormat ) { + this.logDateFormat = logDateFormat; + } + } + } /** diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/DefaultServletWebServerFactoryCustomizer.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/DefaultServletWebServerFactoryCustomizer.java index 702f3771b55..bdfe2857688 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/DefaultServletWebServerFactoryCustomizer.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/DefaultServletWebServerFactoryCustomizer.java @@ -36,6 +36,7 @@ import org.eclipse.jetty.server.AbstractConnector; import org.eclipse.jetty.server.ConnectionFactory; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.NCSARequestLog; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.HandlerCollection; @@ -541,6 +542,9 @@ public class DefaultServletWebServerFactoryCustomizer customizeConnectionTimeout(factory, serverProperties.getConnectionTimeout()); } + if (jettyProperties.getAccesslog().isEnabled()) { + customizeAccesslog(factory, jettyProperties.getAccesslog()); + } } private static void customizeConnectionTimeout( @@ -637,6 +641,32 @@ public class DefaultServletWebServerFactoryCustomizer }); } + + private static void customizeAccesslog(JettyServletWebServerFactory factory, + final ServerProperties.Jetty.Accesslog accesslog) { + factory.addServerCustomizers( server -> { + NCSARequestLog requestLog = new NCSARequestLog(); + if (accesslog.getFilename() != null) { + requestLog.setFilename(accesslog.getFilename()); + } + if (accesslog.getFilenameDateFormat() != null) { + requestLog.setFilenameDateFormat(accesslog.getFilenameDateFormat()); + } + if (accesslog.getRetainDays() > 0) { + requestLog.setRetainDays(accesslog.getRetainDays()); + } + requestLog.setLogTimeZone(accesslog.getLogTimeZone()); + requestLog.setExtended(accesslog.isExtended()); + requestLog.setAppend(accesslog.isAppend()); + requestLog.setLogCookies(accesslog.isLogCookies()); + requestLog.setLogServer( accesslog.isLogServer() ); + requestLog.setLogLatency( accesslog.isLogLatency() ); + if (!StringUtils.isEmpty( accesslog.getLogDateFormat() )) { + requestLog.setLogDateFormat( accesslog.getLogDateFormat() ); + } + server.setRequestLog(requestLog); + }); + } } } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java index 3fd99ba96fd..a9f81860abd 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java @@ -184,6 +184,23 @@ public class ServerPropertiesTests { assertThat(this.properties.getJetty().getSelectors()).isEqualTo(10); } + @Test + public void testJettyBinding() throws Exception { + Map map = new HashMap(); + map.put("server.jetty.accesslog.enabled", "true"); + map.put("server.jetty.accesslog.filename", "foo.txt"); + map.put("server.jetty.accesslog.retainDays", "4"); + map.put("server.jetty.accesslog.append", "true"); + map.put("server.jetty.accesslog.filenameDateFormat", "yyyymmdd"); + bindProperties(map); + ServerProperties.Jetty jetty = this.properties.getJetty(); + assertThat(jetty.getAccesslog().isEnabled()).isEqualTo(true); + assertThat(jetty.getAccesslog().getFilename()).isEqualTo("foo.txt"); + assertThat(jetty.getAccesslog().getRetainDays()).isEqualTo(4); + assertThat(jetty.getAccesslog().isAppend()).isEqualTo(true); + assertThat(jetty.getAccesslog().getFilenameDateFormat()).isEqualTo("yyyymmdd"); + } + private void bindProperties(Map map) { new RelaxedDataBinder(this.properties, "server") .bind(new MutablePropertyValues(map)); 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 c74468b44db..441bdc5e56d 100644 --- a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -159,6 +159,20 @@ content into your application; rather pick only the properties that you need. server.jetty.acceptors= # Number of acceptor threads to use. server.jetty.max-http-post-size=0 # Maximum size in bytes of the HTTP post or put content. server.jetty.selectors= # Number of selector threads to use. + server.jetty.accesslog.enabled=false # Enable access log. + server.jetty.accesslog.filename= # Filename of Jetty access logs. + server.jetty.accesslog.retainDays=31 # number of days before rotated log files are deleted. + server.jetty.accesslog.append=true # append to log flag. + server.jetty.accesslog.filenameDateFormat=yyyy_MM_dd # log file name date format. + server.jetty.accesslog.extended= true # extended request log format flag. + server.jetty.accesslog.logTimeZone= GMT # the timezone of the request log + server.jetty.accesslog.logCookies=false # Controls logging of the request cookies. + server.jetty.accesslog.logServer=false # Controls logging of the request hostname. + server.jetty.accesslog.logLatency=false # Controls logging of request processing time. + server.jetty.accesslog.logDateFormat= # Set the timestamp format for request log entries in the file. If this is not set, the pre-formated request timestamp is used. + server.jsp-servlet.class-name=org.apache.jasper.servlet.JspServlet # The class name of the JSP servlet. + server.jsp-servlet.init-parameters.*= # Init parameters used to configure the JSP servlet + server.jsp-servlet.registered=true # Whether or not the JSP servlet is registered server.port=8080 # Server HTTP port. server.server-header= # Value to use for the Server response header (no header is sent if empty) server.use-forward-headers= # If X-Forwarded-* headers should be applied to the HttpRequest. diff --git a/spring-boot-docs/src/main/asciidoc/howto.adoc b/spring-boot-docs/src/main/asciidoc/howto.adoc index 54b677c0fb1..8abf63694ab 100644 --- a/spring-boot-docs/src/main/asciidoc/howto.adoc +++ b/spring-boot-docs/src/main/asciidoc/howto.adoc @@ -636,7 +636,7 @@ sample project for an example. [[howto-configure-accesslogs]] === Configure Access Logging -Access logs can be configured for Tomcat and Undertow via their respective namespaces. +Access logs can be configured for Tomcat, Undertow and Jetty via their respective namespaces. For instance, the following logs access on Tomcat with a https://tomcat.apache.org/tomcat-8.0-doc/config/valve.html#Access_Logging[custom pattern]. @@ -664,6 +664,16 @@ Access logging for undertow can be configured in a similar fashion Logs are stored in a `logs` directory relative to the working directory of the application. This can be customized via `server.undertow.accesslog.directory`. +Access logging for jetty can be configured in a similar fashion + +[source,properties,indent=0,subs="verbatim,quotes,attributes"] +---- + server.jetty.accesslog.enabled=true + server.jetty.accesslog.filename= +---- + +If no filename logs will go to System.err. +For more details please refer to https://www.eclipse.org/jetty/documentation/9.3.x/configuring-jetty-request-logs.html. [[howto-use-behind-a-proxy-server]]