Apply mgmt access log prefix to reactive Jetty, Tomcat, and Undertow
Fixes gh-44197
This commit is contained in:
@@ -16,19 +16,33 @@
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.web.reactive;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.catalina.Valve;
|
||||
import org.apache.catalina.valves.AccessLogValve;
|
||||
import org.eclipse.jetty.server.CustomRequestLog;
|
||||
import org.eclipse.jetty.server.RequestLog;
|
||||
import org.eclipse.jetty.server.RequestLogWriter;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.ManagementContextType;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerProperties;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementWebServerFactoryCustomizer;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
|
||||
import org.springframework.boot.web.embedded.jetty.JettyReactiveWebServerFactory;
|
||||
import org.springframework.boot.web.embedded.tomcat.TomcatReactiveWebServerFactory;
|
||||
import org.springframework.boot.web.embedded.undertow.UndertowReactiveWebServerFactory;
|
||||
import org.springframework.boot.web.server.ConfigurableWebServerFactory;
|
||||
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.http.server.reactive.ContextPathCompositeHandler;
|
||||
import org.springframework.http.server.reactive.HttpHandler;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -65,4 +79,125 @@ public class ReactiveManagementChildContextConfiguration {
|
||||
return httpHandler;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnClass(name = "io.undertow.Undertow")
|
||||
UndertowAccessLogCustomizer undertowManagementAccessLogCustomizer(ManagementServerProperties properties) {
|
||||
return new UndertowAccessLogCustomizer(properties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnClass(name = "org.apache.catalina.valves.AccessLogValve")
|
||||
TomcatAccessLogCustomizer tomcatManagementAccessLogCustomizer(ManagementServerProperties properties) {
|
||||
return new TomcatAccessLogCustomizer(properties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnClass(name = "org.eclipse.jetty.server.Server")
|
||||
JettyAccessLogCustomizer jettyManagementAccessLogCustomizer(ManagementServerProperties properties) {
|
||||
return new JettyAccessLogCustomizer(properties);
|
||||
}
|
||||
|
||||
abstract static class AccessLogCustomizer implements Ordered {
|
||||
|
||||
private final ManagementServerProperties properties;
|
||||
|
||||
AccessLogCustomizer(ManagementServerProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
protected String customizePrefix(String prefix) {
|
||||
prefix = (prefix != null) ? prefix : "";
|
||||
if (prefix.startsWith(this.properties.getAccesslog().getPrefix())) {
|
||||
return prefix;
|
||||
}
|
||||
return this.properties.getAccesslog().getPrefix() + prefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class TomcatAccessLogCustomizer extends AccessLogCustomizer
|
||||
implements WebServerFactoryCustomizer<TomcatReactiveWebServerFactory> {
|
||||
|
||||
TomcatAccessLogCustomizer(ManagementServerProperties properties) {
|
||||
super(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customize(TomcatReactiveWebServerFactory factory) {
|
||||
System.out.println("Looking for access log valve in " + factory);
|
||||
AccessLogValve accessLogValve = findAccessLogValve(factory);
|
||||
if (accessLogValve == null) {
|
||||
System.out.println("Did not find it");
|
||||
return;
|
||||
}
|
||||
accessLogValve.setPrefix(customizePrefix(accessLogValve.getPrefix()));
|
||||
System.out.println("Customized " + factory);
|
||||
}
|
||||
|
||||
private AccessLogValve findAccessLogValve(TomcatReactiveWebServerFactory factory) {
|
||||
for (Valve engineValve : factory.getEngineValves()) {
|
||||
if (engineValve instanceof AccessLogValve accessLogValve) {
|
||||
return accessLogValve;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class UndertowAccessLogCustomizer extends AccessLogCustomizer
|
||||
implements WebServerFactoryCustomizer<UndertowReactiveWebServerFactory> {
|
||||
|
||||
UndertowAccessLogCustomizer(ManagementServerProperties properties) {
|
||||
super(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customize(UndertowReactiveWebServerFactory factory) {
|
||||
factory.setAccessLogPrefix(customizePrefix(factory.getAccessLogPrefix()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class JettyAccessLogCustomizer extends AccessLogCustomizer
|
||||
implements WebServerFactoryCustomizer<JettyReactiveWebServerFactory> {
|
||||
|
||||
JettyAccessLogCustomizer(ManagementServerProperties properties) {
|
||||
super(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customize(JettyReactiveWebServerFactory factory) {
|
||||
factory.addServerCustomizers(this::customizeServer);
|
||||
}
|
||||
|
||||
private void customizeServer(Server server) {
|
||||
RequestLog requestLog = server.getRequestLog();
|
||||
if (requestLog instanceof CustomRequestLog customRequestLog) {
|
||||
customizeRequestLog(customRequestLog);
|
||||
}
|
||||
}
|
||||
|
||||
private void customizeRequestLog(CustomRequestLog requestLog) {
|
||||
if (requestLog.getWriter() instanceof RequestLogWriter requestLogWriter) {
|
||||
customizeRequestLogWriter(requestLogWriter);
|
||||
}
|
||||
}
|
||||
|
||||
private void customizeRequestLogWriter(RequestLogWriter writer) {
|
||||
String filename = writer.getFileName();
|
||||
if (StringUtils.hasLength(filename)) {
|
||||
File file = new File(filename);
|
||||
file = new File(file.getParentFile(), customizePrefix(file.getName()));
|
||||
writer.setFilename(file.getPath());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -19,8 +19,13 @@ package org.springframework.boot.actuate.autoconfigure.web.reactive;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.apache.catalina.Valve;
|
||||
import org.apache.catalina.startup.Tomcat;
|
||||
import org.apache.catalina.valves.AccessLogValve;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
@@ -39,7 +44,11 @@ import org.springframework.boot.test.context.assertj.AssertableReactiveWebApplic
|
||||
import org.springframework.boot.test.context.runner.ContextConsumer;
|
||||
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
|
||||
import org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer;
|
||||
import org.springframework.boot.web.context.WebServerInitializedEvent;
|
||||
import org.springframework.boot.web.embedded.tomcat.TomcatWebServer;
|
||||
import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext;
|
||||
import org.springframework.boot.web.server.WebServer;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.convert.support.ConfigurableConversionService;
|
||||
import org.springframework.http.MediaType;
|
||||
@@ -55,6 +64,8 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
*/
|
||||
class ReactiveManagementChildContextConfigurationIntegrationTests {
|
||||
|
||||
private final List<WebServer> webServers = new ArrayList<>();
|
||||
|
||||
private final ReactiveWebApplicationContextRunner runner = new ReactiveWebApplicationContextRunner(
|
||||
AnnotationConfigReactiveWebServerApplicationContext::new)
|
||||
.withConfiguration(AutoConfigurations.of(ManagementContextAutoConfiguration.class,
|
||||
@@ -63,6 +74,8 @@ class ReactiveManagementChildContextConfigurationIntegrationTests {
|
||||
WebFluxAutoConfiguration.class))
|
||||
.withUserConfiguration(SucceedingEndpoint.class)
|
||||
.withInitializer(new ServerPortInfoApplicationContextInitializer())
|
||||
.withInitializer((context) -> context.addApplicationListener(
|
||||
(ApplicationListener<WebServerInitializedEvent>) (event) -> this.webServers.add(event.getWebServer())))
|
||||
.withPropertyValues("server.port=0", "management.server.port=0", "management.endpoints.web.exposure.include=*");
|
||||
|
||||
@TempDir
|
||||
@@ -99,6 +112,26 @@ class ReactiveManagementChildContextConfigurationIntegrationTests {
|
||||
.run((context) -> assertThat(context).hasNotFailed());
|
||||
}
|
||||
|
||||
@Test
|
||||
void accessLogHasManagementServerSpecificPrefix() {
|
||||
this.runner.withPropertyValues("server.tomcat.accesslog.enabled=true").run((context) -> {
|
||||
AccessLogValve accessLogValve = findAccessLogValve();
|
||||
assertThat(accessLogValve).isNotNull();
|
||||
assertThat(accessLogValve.getPrefix()).isEqualTo("management_access_log");
|
||||
});
|
||||
}
|
||||
|
||||
private AccessLogValve findAccessLogValve() {
|
||||
assertThat(this.webServers).hasSize(2);
|
||||
Tomcat tomcat = ((TomcatWebServer) this.webServers.get(1)).getTomcat();
|
||||
for (Valve valve : tomcat.getEngine().getPipeline().getValves()) {
|
||||
if (valve instanceof AccessLogValve accessLogValve) {
|
||||
return accessLogValve;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void addConfigTreePropertySource(ConfigurableApplicationContext applicationContext) {
|
||||
try {
|
||||
applicationContext.getEnvironment()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2024 the original author or authors.
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -112,6 +112,10 @@ public class UndertowReactiveWebServerFactory extends AbstractReactiveWebServerF
|
||||
this.delegate.setAccessLogPattern(accessLogPattern);
|
||||
}
|
||||
|
||||
public String getAccessLogPrefix() {
|
||||
return this.delegate.getAccessLogPrefix();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAccessLogPrefix(String accessLogPrefix) {
|
||||
this.delegate.setAccessLogPrefix(accessLogPrefix);
|
||||
|
||||
Reference in New Issue
Block a user