package io.cm.accesslog; import io.undertow.Undertow; import io.undertow.UndertowOptions; import io.undertow.server.handlers.accesslog.AccessLogHandler; import io.undertow.server.handlers.accesslog.AccessLogReceiver; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.StringUtils; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.boot.web.embedded.undertow.ConfigurableUndertowWebServerFactory; import org.springframework.boot.web.server.WebServerFactoryCustomizer; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @SpringBootApplication public class UndertowAccessLogApplication { public static void main(String[] args) { SpringApplication.run(UndertowAccessLogApplication.class, args); } } @RestController class HelloController { @RequestMapping("/") public String index() { return "Hello world!"; } } @Component class UndertowWebServerAccessLogTimingEnabler implements WebServerFactoryCustomizer { private final ServerProperties serverProperties; public UndertowWebServerAccessLogTimingEnabler(ServerProperties serverProperties) { this.serverProperties = serverProperties; } @Override public void customize(ConfigurableUndertowWebServerFactory factory) { String pattern = serverProperties.getUndertow().getAccesslog().getPattern(); // Record request timing only if the pattern if (logRequestProcessingTiming(pattern)) { factory.addBuilderCustomizers(builder -> builder.setServerOption(UndertowOptions.RECORD_REQUEST_START_TIME, true)); } // Custom access log receiver. factory.addDeploymentInfoCustomizers(deploymentInfo -> { deploymentInfo.addInitialHandlerChainWrapper(handler -> { Slf4jAccessLogReceiver accessLogReceiver = new Slf4jAccessLogReceiver(); return new AccessLogHandler(handler, accessLogReceiver, pattern, Undertow.class.getClassLoader()); }); }); } private boolean logRequestProcessingTiming(String pattern) { if (StringUtils.isBlank(pattern)) { return false; } return pattern.contains("%D") || pattern.contains("%T"); } } /** * Direct access logs to slf4j. Can be sent to custom location with logger configuration. * Default undertow access logs are in the launchDirectory/logs of the process */ @Slf4j(topic = "ACCESS_LOG") class Slf4jAccessLogReceiver implements AccessLogReceiver { @Override public void logMessage(String message) { log.info(message); } }