Reputation: 1546
I'm building a web app using Spring Boot 1.1.5.RELEASE and I've configured an embedded Jetty as described in the related Spring Boot's documentation.
I want to log all incoming HTTP requests and the only solution I can think (after reading "how to configure Jetty" in Spring Boot's docs) is to introduce an EmbeddedServletContainerCustomizer:
package com.acme.rest;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.NCSARequestLog;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.DefaultHandler;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.server.handler.RequestLogHandler;
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.jetty.JettyServerCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
/**
* blah blah
*
* @author Dimi
*/
@Component
public class EmbededJettyConfig implements EmbeddedServletContainerCustomizer {
/*
* (non-Javadoc)
*
* @see org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer # customize
*/
@Override
public void customize(final ConfigurableEmbeddedServletContainer container) {
// checks whether the container is Jetty
if (container instanceof JettyEmbeddedServletContainerFactory) {
((JettyEmbeddedServletContainerFactory) container)
.addServerCustomizers(jettyServerCustomizer());
}
}
@Bean
public JettyServerCustomizer jettyServerCustomizer() {
return new JettyServerCustomizer() {
/*
* (non-Javadoc)
*
* @see org.springframework.boot.context.embedded.jetty.JettyServerCustomizer #
* customize
*/
@Override
public void customize(final Server server) {
HandlerCollection handlers = new HandlerCollection();
RequestLogHandler requestLogHandler = new RequestLogHandler();
handlers.setHandlers(new Handler[] {new DefaultHandler(), requestLogHandler});
server.setHandler(handlers);
NCSARequestLog requestLog = new NCSARequestLog("logs/requests.log");
requestLog.setExtended(false);
requestLogHandler.setRequestLog(requestLog);
}
};
}
}
The application now fails to start throwing an exception:
java.lang.IllegalArgumentException: A ServletContext is required to configure default servlet handling
In short: what is the correct way of configuring the embedded Jetty's logging in Spring Boot?
Upvotes: 3
Views: 6667
Reputation: 24510
Jetty 9.3.8 has a new method setRequestLog
on the server.
@Component
public class EnableRequestLog implements EmbeddedServletContainerCustomizer {
private static final JettyServerCustomizer USE_SLF4J_REQUEST_LOG =
server -> server.setRequestLog(new Slf4jRequestLog());
@Override
public void customize(ConfigurableEmbeddedServletContainer container) {
if (container instanceof JettyEmbeddedServletContainerFactory) {
((JettyEmbeddedServletContainerFactory) container)
.addServerCustomizers(USE_SLF4J_REQUEST_LOG);
} else {
throw new IllegalArgumentException(
"Expected a Jetty container factory but encountered " + container.getClass());
}
}
}
Instead of Jetty's Slf4jRequestLog you can implement a specific RequestLog
. See http://www.eclipse.org/jetty/documentation/current/configuring-jetty-request-logs.html
Upvotes: 5
Reputation: 824
Coming a bit late, but I experienced the same problem and the solution was to wrap the ServletHandler already created by boot in the RequestLogHandler like this:
@Component
public class EmbededJettyConfig implements EmbeddedServletContainerCustomizer {
/*
* (non-Javadoc)
*
* @see org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer # customize
*/
@Override
public void customize(final ConfigurableEmbeddedServletContainer container) {
// checks whether the container is Jetty
if (container instanceof JettyEmbeddedServletContainerFactory) {
((JettyEmbeddedServletContainerFactory) container)
.addServerCustomizers(jettyServerCustomizer());
}
}
@Bean
public JettyServerCustomizer jettyServerCustomizer() {
return new JettyServerCustomizer() {
/*
* (non-Javadoc)
*
* @see org.springframework.boot.context.embedded.jetty.JettyServerCustomizer #
* customize
*/
@Override
public void customize(final Server server) {
NCSARequestLog requestLog = new NCSARequestLog("logs/requests.log");
requestLog.setExtended(false);
RequestLogHandler requestLogHandler = new RequestLogHandler();
requestLogHandler.setRequestLog(requestLog);
requestLogHandler.setHandler(server.getHandler());
server.setHandler(requestLogHandler);
}
};
}
Upvotes: 1
Reputation: 1
This doesn't work with Spring Boot version 1.1.9 because JettyEmbeddedServletContainer handlers only JettyEmbeddedWebAppContext (a handler) or HandlerWrapper. I think there is no way to set multiple handler to embedded Jetty in Spring Boot because of this.
Code snippet from org.springframework.boot.context.embedded.jettyJettyEmbeddedServletContainer.
@Override
public void start() throws EmbeddedServletContainerException {
this.server.setConnectors(this.connectors);
if (!this.autoStart) {
return;
}
try {
this.server.start();
for (Handler handler : this.server.getHandlers()) {
handleDeferredInitialize(handler);
}
Connector[] connectors = this.server.getConnectors();
for (Connector connector : connectors) {
connector.start();
this.logger.info("Jetty started on port: " + getLocalPort(connector));
}
}
catch (Exception ex) {
throw new EmbeddedServletContainerException(
"Unable to start embedded Jetty servlet container", ex);
}
}
private void handleDeferredInitialize(Handler handler) throws Exception {
if (handler instanceof JettyEmbeddedWebAppContext) {
((JettyEmbeddedWebAppContext) handler).deferredInitialize();
}
else if (handler instanceof HandlerWrapper) {
handleDeferredInitialize(((HandlerWrapper) handler).getHandler());
}
}
Upvotes: -1