Merge pull request #8819 from olamy:feature/jetty_accesslog

* pr/8819:
  Add support for configuring Jetty's request log via the environment
  Add support for configuring Jetty's request log via the environment
This commit is contained in:
Stephane Nicoll 2017-04-27 10:47:04 +02:00
commit b426bba169
6 changed files with 320 additions and 1 deletions

View File

@ -23,8 +23,10 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.DeprecatedConfigurationProperty;
@ -48,6 +50,7 @@ import org.springframework.util.StringUtils;
* @author Venil Noronha
* @author Aurélien Leboulanger
* @author Brian Clozel
* @author Olivier Lamy
*/
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
@ -885,6 +888,11 @@ public class ServerProperties {
*/
public static class Jetty {
/**
* Access log configuration.
*/
private final Accesslog accesslog = new Accesslog();
/**
* Maximum size in bytes of the HTTP post or put content.
*/
@ -900,6 +908,10 @@ public class ServerProperties {
*/
private Integer selectors;
public Accesslog getAccesslog() {
return this.accesslog;
}
public int getMaxHttpPostSize() {
return this.maxHttpPostSize;
}
@ -924,6 +936,168 @@ public class ServerProperties {
this.selectors = selectors;
}
/**
* Jetty access log properties.
*/
public static class Accesslog {
/**
* Enable access log.
*/
private boolean enabled = false;
/**
* Log filename. If not specified, logs will be redirected to "System.err".
*/
private String filename;
/**
* Date format to place in log file name.
*/
private String fileDateFormat;
/**
* Number of days before rotated log files are deleted.
*/
private int retentionPeriod = 31; // no days
/**
* Append to log.
*/
private boolean append;
/**
* Enable extended NCSA format.
*/
private boolean extendedFormat;
/**
* Timestamp format of the request log.
*/
private String dateFormat = "dd/MMM/yyyy:HH:mm:ss Z";
/**
* Locale of the request log.
*/
private Locale locale;
/**
* Timezone of the request log.
*/
private TimeZone timeZone = TimeZone.getTimeZone("GMT");
/**
* Enable logging of the request cookies.
*/
private boolean logCookies;
/**
* Enable logging of the request hostname.
*/
private boolean logServer;
/**
* Enable logging of request processing time.
*/
private boolean logLatency;
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 String getFileDateFormat() {
return this.fileDateFormat;
}
public void setFileDateFormat(String fileDateFormat) {
this.fileDateFormat = fileDateFormat;
}
public int getRetentionPeriod() {
return this.retentionPeriod;
}
public void setRetentionPeriod(int retentionPeriod) {
this.retentionPeriod = retentionPeriod;
}
public boolean isAppend() {
return this.append;
}
public void setAppend(boolean append) {
this.append = append;
}
public boolean isExtendedFormat() {
return this.extendedFormat;
}
public void setExtendedFormat(boolean extendedFormat) {
this.extendedFormat = extendedFormat;
}
public String getDateFormat() {
return this.dateFormat;
}
public void setDateFormat(String dateFormat) {
this.dateFormat = dateFormat;
}
public Locale getLocale() {
return this.locale;
}
public void setLocale(Locale locale) {
this.locale = locale;
}
public TimeZone getTimeZone() {
return this.timeZone;
}
public void setTimeZone(TimeZone timeZone) {
this.timeZone = timeZone;
}
public boolean isLogCookies() {
return this.logCookies;
}
public void setLogCookies(boolean logCookies) {
this.logCookies = logCookies;
}
public boolean isLogServer() {
return this.logServer;
}
public void setLogServer(boolean logServer) {
this.logServer = logServer;
}
public boolean isLogLatency() {
return this.logLatency;
}
public void setLogLatency(boolean logLatency) {
this.logLatency = logLatency;
}
}
}
/**

View File

@ -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;
@ -66,6 +67,7 @@ import org.springframework.util.StringUtils;
*
* @author Brian Clozel
* @author Stephane Nicoll
* @author Olivier Lamy
* @since 2.0.0
*/
public class DefaultServletWebServerFactoryCustomizer
@ -541,6 +543,9 @@ public class DefaultServletWebServerFactoryCustomizer
customizeConnectionTimeout(factory,
serverProperties.getConnectionTimeout());
}
if (jettyProperties.getAccesslog().isEnabled()) {
customizeAccesslog(factory, jettyProperties.getAccesslog());
}
}
private static void customizeConnectionTimeout(
@ -637,6 +642,35 @@ 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.getFileDateFormat() != null) {
requestLog.setFilenameDateFormat(accesslog.getFileDateFormat());
}
requestLog.setRetainDays(accesslog.getRetentionPeriod());
requestLog.setAppend(accesslog.isAppend());
requestLog.setExtended(accesslog.isExtendedFormat());
if (accesslog.getDateFormat() != null) {
requestLog.setLogDateFormat(accesslog.getDateFormat());
}
if (accesslog.getLocale() != null) {
requestLog.setLogLocale(accesslog.getLocale());
}
if (accesslog.getTimeZone() != null) {
requestLog.setLogTimeZone(accesslog.getTimeZone().getID());
}
requestLog.setLogCookies(accesslog.isLogCookies());
requestLog.setLogServer(accesslog.isLogServer());
requestLog.setLogLatency(accesslog.isLogLatency());
server.setRequestLog(requestLog);
});
}
}
}

View File

@ -184,6 +184,23 @@ public class ServerPropertiesTests {
assertThat(this.properties.getJetty().getSelectors()).isEqualTo(10);
}
@Test
public void testCustomizeJettyAccessLog() throws Exception {
Map<String, String> map = new HashMap<String, String>();
map.put("server.jetty.accesslog.enabled", "true");
map.put("server.jetty.accesslog.filename", "foo.txt");
map.put("server.jetty.accesslog.file-date-format", "yyyymmdd");
map.put("server.jetty.accesslog.retention-period", "4");
map.put("server.jetty.accesslog.append", "true");
bindProperties(map);
ServerProperties.Jetty jetty = this.properties.getJetty();
assertThat(jetty.getAccesslog().isEnabled()).isEqualTo(true);
assertThat(jetty.getAccesslog().getFilename()).isEqualTo("foo.txt");
assertThat(jetty.getAccesslog().getFileDateFormat()).isEqualTo("yyyymmdd");
assertThat(jetty.getAccesslog().getRetentionPeriod()).isEqualTo(4);
assertThat(jetty.getAccesslog().isAppend()).isEqualTo(true);
}
private void bindProperties(Map<String, String> map) {
new RelaxedDataBinder(this.properties, "server")
.bind(new MutablePropertyValues(map));

View File

@ -20,6 +20,7 @@ import java.io.File;
import java.net.URL;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import javax.servlet.ServletContext;
@ -33,6 +34,8 @@ import org.apache.catalina.valves.AccessLogValve;
import org.apache.catalina.valves.RemoteIpValve;
import org.apache.catalina.webresources.TomcatURLStreamHandlerFactory;
import org.apache.coyote.AbstractProtocol;
import org.eclipse.jetty.server.NCSARequestLog;
import org.eclipse.jetty.server.RequestLog;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
@ -45,6 +48,7 @@ import org.springframework.beans.MutablePropertyValues;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.bind.RelaxedDataBinder;
import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory;
import org.springframework.boot.web.embedded.jetty.JettyWebServer;
import org.springframework.boot.web.embedded.tomcat.TomcatContextCustomizer;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.embedded.tomcat.TomcatWebServer;
@ -527,6 +531,72 @@ public class DefaultServletWebServerFactoryCustomizerTests {
verify(factory).setSessionStoreDir(new File("myfolder"));
}
@Test
public void jettyAccessLogCanBeEnabled() {
JettyServletWebServerFactory factory = new JettyServletWebServerFactory(0);
Map<String, String> map = new HashMap<>();
map.put("server.jetty.accesslog.enabled", "true");
bindProperties(map);
this.customizer.customize(factory);
JettyWebServer webServer = (JettyWebServer) factory.getWebServer();
try {
NCSARequestLog requestLog = getNCSARequestLog(webServer);
assertThat(requestLog.getFilename()).isNull();
assertThat(requestLog.isAppend()).isFalse();
assertThat(requestLog.isExtended()).isFalse();
assertThat(requestLog.getLogCookies()).isFalse();
assertThat(requestLog.getLogServer()).isFalse();
assertThat(requestLog.getLogLatency()).isFalse();
}
finally {
webServer.stop();
}
}
@Test
public void jettyAccessLogCanBeCustomized() {
JettyServletWebServerFactory factory = new JettyServletWebServerFactory(0);
Map<String, String> map = new HashMap<>();
map.put("server.jetty.accesslog.enabled", "true");
map.put("server.jetty.accesslog.filename", "foo");
map.put("server.jetty.accesslog.file-date-format", "yyyy-MM-dd");
map.put("server.jetty.accesslog.retention-period", "42");
map.put("server.jetty.accesslog.append", "true");
map.put("server.jetty.accesslog.extended-format", "true");
map.put("server.jetty.accesslog.date-format", "HH:mm:ss");
map.put("server.jetty.accesslog.locale", "en_BE");
map.put("server.jetty.accesslog.time-zone", "UTC");
map.put("server.jetty.accesslog.log-cookies", "true");
map.put("server.jetty.accesslog.log-server", "true");
map.put("server.jetty.accesslog.log-latency", "true");
bindProperties(map);
this.customizer.customize(factory);
JettyWebServer webServer = (JettyWebServer) factory.getWebServer();
NCSARequestLog requestLog = getNCSARequestLog(webServer);
try {
assertThat(requestLog.getFilename()).isEqualTo("foo");
assertThat(requestLog.getFilenameDateFormat()).isEqualTo("yyyy-MM-dd");
assertThat(requestLog.getRetainDays()).isEqualTo(42);
assertThat(requestLog.isAppend()).isTrue();
assertThat(requestLog.isExtended()).isTrue();
assertThat(requestLog.getLogDateFormat()).isEqualTo("HH:mm:ss");
assertThat(requestLog.getLogLocale()).isEqualTo(new Locale("en", "BE"));
assertThat(requestLog.getLogTimeZone()).isEqualTo("UTC");
assertThat(requestLog.getLogCookies()).isTrue();
assertThat(requestLog.getLogServer()).isTrue();
assertThat(requestLog.getLogLatency()).isTrue();
}
finally {
webServer.stop();
}
}
private NCSARequestLog getNCSARequestLog(JettyWebServer webServer) {
RequestLog requestLog = webServer.getServer().getRequestLog();
assertThat(requestLog).isInstanceOf(NCSARequestLog.class);
return (NCSARequestLog) requestLog;
}
@Test
public void skipNullElementsForUndertow() throws Exception {
UndertowServletWebServerFactory factory = mock(

View File

@ -157,6 +157,18 @@ content into your application; rather pick only the properties that you need.
server.error.path=/error # Path of the error controller.
server.error.whitelabel.enabled=true # Enable the default error page displayed in browsers in case of a server error.
server.jetty.acceptors= # Number of acceptor threads to use.
server.jetty.accesslog.append=false # Append to log.
server.jetty.accesslog.date-format=dd/MMM/yyyy:HH:mm:ss Z # Timestamp format of the request log.
server.jetty.accesslog.enabled=false # Enable access log.
server.jetty.accesslog.extended-format=false # Enable extended NCSA format.
server.jetty.accesslog.file-date-format= # Date format to place in log file name.
server.jetty.accesslog.filename= # Log filename. If not specified, logs will be redirected to "System.err".
server.jetty.accesslog.locale= # Locale of the request log.
server.jetty.accesslog.log-cookies=false # Enable logging of the request cookies.
server.jetty.accesslog.log-latency=false # Enable logging of request processing time.
server.jetty.accesslog.log-server=false # Enable logging of the request hostname.
server.jetty.accesslog.retention-period=31 # Number of days before rotated log files are deleted.
server.jetty.accesslog.time-zone=GMT # Timezone of the request log.
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.port=8080 # Server HTTP port.

View File

@ -636,7 +636,8 @@ 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 +665,17 @@ 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`.
Finally, access logging for jetty can also be configured that way:
[source,properties,indent=0,subs="verbatim,quotes,attributes"]
----
server.jetty.accesslog.enabled=true
server.jetty.accesslog.filename=access-log
----
By default, logs will be redirected to `System.err`. For more details, please refer to
https://www.eclipse.org/jetty/documentation/9.3.x/configuring-jetty-request-logs.html[the documentation].
[[howto-use-behind-a-proxy-server]]