dimi
dimi

Reputation: 1546

Spring Boot: Log all incoming HTTP requests when using an embedded Jetty server?

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

Answers (3)

Stefan Birkner
Stefan Birkner

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

vmihaylov76
vmihaylov76

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

Nikke K
Nikke K

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

Related Questions