Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for access log for each servlet server implementation #713

Merged
merged 4 commits into from
May 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,19 @@
import io.micronaut.context.annotation.ConfigurationBuilder;
import io.micronaut.context.annotation.ConfigurationProperties;
import io.micronaut.context.annotation.Replaces;
import io.micronaut.context.annotation.Requires;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.convert.format.MapFormat;
import io.micronaut.core.naming.conventions.StringConvention;
import io.micronaut.core.util.StringUtils;
import io.micronaut.core.util.Toggleable;
import io.micronaut.http.server.HttpServerConfiguration;
import jakarta.inject.Inject;
import org.eclipse.jetty.server.CustomRequestLog;
import org.eclipse.jetty.server.HttpConfiguration;

import io.micronaut.core.annotation.Nullable;
import org.eclipse.jetty.server.RequestLogWriter;
import org.eclipse.jetty.server.SecureRequestCustomizer;

import java.util.Collections;
Expand All @@ -42,6 +49,7 @@ public class JettyConfiguration extends HttpServerConfiguration {

@ConfigurationBuilder
protected HttpConfiguration httpConfiguration = new HttpConfiguration();
private final JettyRequestLog requestLog;

private final MultipartConfiguration multipartConfiguration;
private Map<String, String> initParameters;
Expand All @@ -51,7 +59,17 @@ public class JettyConfiguration extends HttpServerConfiguration {
* @param multipartConfiguration The multipart configuration.
*/
public JettyConfiguration(@Nullable MultipartConfiguration multipartConfiguration) {
this(null, null);
}

/**
* Default constructor.
* @param multipartConfiguration The multipart configuration.
*/
@Inject
public JettyConfiguration(@Nullable MultipartConfiguration multipartConfiguration, @Nullable JettyRequestLog requestLog) {
this.multipartConfiguration = multipartConfiguration;
this.requestLog = requestLog;
}

/**
Expand All @@ -68,6 +86,13 @@ public Optional<MultipartConfiguration> getMultipartConfiguration() {
return Optional.ofNullable(multipartConfiguration);
}

/**
* @return The request log configuration.
*/
public Optional<JettyRequestLog> getRequestLog() {
return Optional.ofNullable(requestLog);
}

/**
* @return The servlet init parameters
*/
Expand Down Expand Up @@ -98,4 +123,51 @@ public void setInitParameters(
public static class JettySslConfiguration extends SecureRequestCustomizer {
}

/**
* Jetty access log configuration.
*
* @since 4.8.0
*/
@ConfigurationProperties(JettyRequestLog.ACCESS_LOG)
@Requires(property = JettyRequestLog.ENABLED_PROPERTY, value = StringUtils.TRUE)
public static class JettyRequestLog implements Toggleable {
public static final String ACCESS_LOG = "access-log";
public static final String ENABLED_PROPERTY = HttpServerConfiguration.PREFIX + ".jetty." + ACCESS_LOG + ".enabled";
@ConfigurationBuilder(prefixes = "set", excludes = "eventListeners")
RequestLogWriter requestLogWriter = new RequestLogWriter();

private boolean enabled = true;
private String pattern = CustomRequestLog.EXTENDED_NCSA_FORMAT;

@Override
public boolean isEnabled() {
return enabled;
}

/**
* Whether access log is enabled.
* @param enabled True if it is enabled.
*/
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}

/**
* The pattern to use for the access log. Defaults to {@code EXTENDED_NCSA_FORMAT}.
*
* @return The pattern.
*/
public @NonNull String getPattern() {
return pattern;
}

/**
* Sets the pattern to use for the access log. Defaults to CustomRequestLog.EXTENDED_NCSA_FORMAT.
*
* @param pattern The pattern
*/
public void setPattern(String pattern) {
this.pattern = pattern;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory;
import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
import org.eclipse.jetty.server.ConnectionFactory;
import org.eclipse.jetty.server.CustomRequestLog;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.Server;
Expand Down Expand Up @@ -147,6 +148,15 @@ protected Server jettyServer(

Server server = newServer(applicationContext, configuration);

jettyConfiguration.getRequestLog().ifPresent(requestLog -> {
if (requestLog.isEnabled()) {
server.setRequestLog(new CustomRequestLog(
requestLog.requestLogWriter,
requestLog.getPattern()
));
}
});

final ServletContextHandler contextHandler = newJettyContext(server, contextPath);
configureServletInitializer(server, contextHandler, servletContainerInitializers);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package io.micronaut.servlet.jetty

import io.micronaut.test.extensions.spock.annotation.MicronautTest
import io.micronaut.test.support.TestPropertyProvider
import jakarta.inject.Inject
import org.eclipse.jetty.server.CustomRequestLog
import org.eclipse.jetty.server.Server
import spock.lang.Specification

import java.nio.file.Files

@MicronautTest
class JettyAccessLogSpec extends Specification implements TestPropertyProvider {
@Inject
JettyConfiguration.JettyRequestLog requestLog

@Inject Server server

void "test configuration"() {
expect:
requestLog.enabled
requestLog.requestLogWriter.retainDays == 10
requestLog.requestLogWriter.fileName != null
requestLog.pattern == CustomRequestLog.NCSA_FORMAT
server.requestLog != null
}

@Override
Map<String, String> getProperties() {
return [
"micronaut.server.jetty.access-log.enabled": true,
"micronaut.server.jetty.access-log.filename": Files.createTempFile('log', 'test').toAbsolutePath().toString(),
"micronaut.server.jetty.access-log.retain-days": 10,
"micronaut.server.jetty.access-log.pattern": CustomRequestLog.NCSA_FORMAT
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
*/
package io.micronaut.servlet.tomcat;

import io.micronaut.context.annotation.Requires;
import io.micronaut.core.util.StringUtils;
import io.micronaut.core.util.Toggleable;
import jakarta.inject.Inject;
import java.util.Optional;

import io.micronaut.context.annotation.ConfigurationBuilder;
Expand All @@ -25,6 +29,7 @@
import io.micronaut.core.annotation.TypeHint;
import io.micronaut.http.server.HttpServerConfiguration;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.valves.ExtendedAccessLogValve;
import org.apache.coyote.ajp.AjpNio2Protocol;
import org.apache.coyote.ajp.AjpNioProtocol;
import org.apache.coyote.http11.Http11Nio2Protocol;
Expand Down Expand Up @@ -54,6 +59,8 @@ public class TomcatConfiguration extends HttpServerConfiguration {
private final MultipartConfiguration multipartConfiguration;
private String protocol;

private AccessLogConfiguration accessLogConfiguration;

/**
* Default constructor.
* @param multipartConfiguration The multipart config
Expand Down Expand Up @@ -97,4 +104,40 @@ public Optional<MultipartConfiguration> getMultipartConfiguration() {
return Optional.ofNullable(multipartConfiguration);
}

/**
* @return The access log configuration.
* @since 4.8.0
*/
public Optional<AccessLogConfiguration> getAccessLogConfiguration() {
return Optional.ofNullable(accessLogConfiguration);
}

/**
* Sets the access log configuration.
* @param accessLogConfiguration The access log configuration.
* @since 4.8.0
*/
@Inject
public void setAccessLogConfiguration(@Nullable AccessLogConfiguration accessLogConfiguration) {
this.accessLogConfiguration = accessLogConfiguration;
}

/**
* The access log configuration.
* @since 4.8.0
*/
@ConfigurationProperties(value = AccessLogConfiguration.PREFIX, excludes = {"next", "container"})
@Requires(property = AccessLogConfiguration.ENABLED_PROPERTY, value = StringUtils.TRUE)
@SuppressWarnings("java:S110")
public static class AccessLogConfiguration extends ExtendedAccessLogValve implements Toggleable {

public static final String PREFIX = "access-log";

public static final String ENABLED_PROPERTY = HttpServerConfiguration.PREFIX + ".tomcat." + PREFIX + ".enabled";

@Override
public boolean isEnabled() {
return super.enabled;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,10 @@
import io.micronaut.servlet.engine.server.ServletStaticResourceConfiguration;
import jakarta.inject.Singleton;
import java.util.Set;
import org.apache.catalina.Container;
import org.apache.catalina.Context;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.core.ContainerBase;
import org.apache.catalina.startup.Tomcat;
import org.apache.coyote.ProtocolHandler;
import org.apache.coyote.http2.Http2Protocol;
Expand Down Expand Up @@ -127,6 +129,18 @@ protected Tomcat tomcatServer(
configureServletInitializer(context, servletInitializers);
configureConnectors(tomcat, connector, httpsConnector);

TomcatConfiguration serverConfiguration = getServerConfiguration();
serverConfiguration.getAccessLogConfiguration().ifPresent(accessValve -> {
if (accessValve.isEnabled()) {
Container[] children = tomcat.getHost().findChildren();
for (Container child : children) {
if (child instanceof ContainerBase containerBase) {
containerBase.addValve(accessValve);
}
}
}
});

return tomcat;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package io.micronaut.servlet.tomcat

import io.micronaut.test.extensions.spock.annotation.MicronautTest
import io.micronaut.test.support.TestPropertyProvider
import jakarta.inject.Inject
import org.apache.catalina.Valve
import org.apache.catalina.startup.Tomcat
import org.apache.catalina.valves.Constants
import spock.lang.Specification

import java.nio.file.Files

@MicronautTest
class TomcatAccessLogSpec extends Specification implements TestPropertyProvider {
@Inject TomcatConfiguration.AccessLogConfiguration accessLogConfiguration
@Inject Tomcat tomcat

void "test access log"() {
expect:
accessLogConfiguration.enabled
accessLogConfiguration.pattern == Constants.AccessLog.COMBINED_PATTERN
def valves = tomcat.host.findChildren().first().pipeline.valves
valves
valves.first() instanceof TomcatConfiguration.AccessLogConfiguration
}

@Override
Map<String, String> getProperties() {
return [
"micronaut.server.tomcat.access-log.enabled": true,
"micronaut.server.tomcat.access-log.pattern": Constants.AccessLog.COMBINED_PATTERN
]
}
}
Loading
Loading