Reorganize server observability packages

Prior to this commit, the server observability support would create a
cycle in Java packages.

This commit refactors the current arrangement to solve this by:

* "flattening" the reactive HTTP instrumentation; this removes the
  dependency to the `ServerWebExchange` and `PathPattern` types
* moving the `observation` package under
  `org.springframework.http.server` and
  `org.springframework.http.server.reactive`

See gh-29477
This commit is contained in:
Brian Clozel
2022-11-14 12:59:36 +01:00
parent 91c6fac18a
commit 1ad7cc3702
24 changed files with 87 additions and 94 deletions

View File

@@ -1,4 +1,4 @@
version=6.0.0-SNAPSHOT
version=6.0.0-RC4
org.gradle.caching=true
org.gradle.jvmargs=-Xmx2048m

View File

@@ -14,15 +14,15 @@
* limitations under the License.
*/
package org.springframework.http.observation;
package org.springframework.http.server.observation;
import io.micrometer.common.KeyValue;
import io.micrometer.common.KeyValues;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.observation.ServerHttpObservationDocumentation.HighCardinalityKeyNames;
import org.springframework.http.observation.ServerHttpObservationDocumentation.LowCardinalityKeyNames;
import org.springframework.http.server.observation.ServerHttpObservationDocumentation.HighCardinalityKeyNames;
import org.springframework.http.server.observation.ServerHttpObservationDocumentation.LowCardinalityKeyNames;
import org.springframework.util.StringUtils;
/**

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.http.observation;
package org.springframework.http.server.observation;
import io.micrometer.common.KeyValue;
import io.micrometer.common.docs.KeyName;
@@ -33,9 +33,9 @@ import io.micrometer.observation.docs.ObservationDocumentation;
public enum ServerHttpObservationDocumentation implements ObservationDocumentation {
/**
* HTTP exchanges observations for Servlet-based servers.
* HTTP request observations for Servlet-based servers.
*/
HTTP_SERVLET_SERVER_EXCHANGES {
HTTP_SERVLET_SERVER_REQUESTS {
@Override
public Class<? extends ObservationConvention<? extends Observation.Context>> getDefaultConvention() {
return DefaultServerRequestObservationConvention.class;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.http.observation;
package org.springframework.http.server.observation;
import io.micrometer.observation.transport.RequestReplyReceiverContext;
import jakarta.servlet.http.HttpServletRequest;
@@ -23,10 +23,10 @@ import jakarta.servlet.http.HttpServletResponse;
import org.springframework.lang.Nullable;
/**
* Context that holds information for metadata collection during observations
* for {@link ServerHttpObservationDocumentation#HTTP_SERVLET_SERVER_EXCHANGES Servlet HTTP exchanges}.
* Context that holds information for metadata collection regarding
* {@link ServerHttpObservationDocumentation#HTTP_SERVLET_SERVER_REQUESTS Servlet HTTP requests} observations.
* <p>This context also extends {@link RequestReplyReceiverContext} for propagating
* tracing information with the HTTP server exchange.
* tracing information during HTTP request processing.
*
* @author Brian Clozel
* @since 6.0

View File

@@ -14,13 +14,13 @@
* limitations under the License.
*/
package org.springframework.http.observation;
package org.springframework.http.server.observation;
import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationConvention;
/**
* Interface for an {@link ObservationConvention} for {@link ServerHttpObservationDocumentation#HTTP_SERVLET_SERVER_EXCHANGES Servlet HTTP exchanges}.
* Interface for an {@link ObservationConvention} for {@link ServerHttpObservationDocumentation#HTTP_SERVLET_SERVER_REQUESTS Servlet HTTP requests}.
*
* @author Brian Clozel
* @since 6.0

View File

@@ -1,9 +1,9 @@
/**
* Instrumentation for {@link io.micrometer.observation.Observation observing} HTTP applications.
* Instrumentation for {@link io.micrometer.observation.Observation observing} HTTP server applications.
*/
@NonNullApi
@NonNullFields
package org.springframework.http.observation;
package org.springframework.http.server.observation;
import org.springframework.lang.NonNullApi;
import org.springframework.lang.NonNullFields;

View File

@@ -14,17 +14,16 @@
* limitations under the License.
*/
package org.springframework.http.observation.reactive;
package org.springframework.http.server.reactive.observation;
import io.micrometer.common.KeyValue;
import io.micrometer.common.KeyValues;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.observation.reactive.ServerHttpObservationDocumentation.HighCardinalityKeyNames;
import org.springframework.http.observation.reactive.ServerHttpObservationDocumentation.LowCardinalityKeyNames;
import org.springframework.http.server.reactive.observation.ServerHttpObservationDocumentation.HighCardinalityKeyNames;
import org.springframework.http.server.reactive.observation.ServerHttpObservationDocumentation.LowCardinalityKeyNames;
import org.springframework.util.StringUtils;
import org.springframework.web.util.pattern.PathPattern;
/**
* Default {@link ServerRequestObservationConvention}.
@@ -117,12 +116,12 @@ public class DefaultServerRequestObservationConvention implements ServerRequestO
protected KeyValue uri(ServerRequestObservationContext context) {
if (context.getCarrier() != null) {
PathPattern pattern = context.getPathPattern();
String pattern = context.getPathPattern();
if (pattern != null) {
if (pattern.toString().isEmpty()) {
if (pattern.isEmpty()) {
return URI_ROOT;
}
return KeyValue.of(LowCardinalityKeyNames.URI, pattern.toString());
return KeyValue.of(LowCardinalityKeyNames.URI, pattern);
}
if (context.getResponse() != null && context.getResponse().getStatusCode() != null) {
HttpStatus status = HttpStatus.resolve(context.getResponse().getStatusCode().value());

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.http.observation.reactive;
package org.springframework.http.server.reactive.observation;
import io.micrometer.common.KeyValue;
import io.micrometer.common.docs.KeyName;
@@ -33,9 +33,9 @@ import io.micrometer.observation.docs.ObservationDocumentation;
public enum ServerHttpObservationDocumentation implements ObservationDocumentation {
/**
* HTTP exchanges observations for reactive servers.
* HTTP request observations for reactive servers.
*/
HTTP_REACTIVE_SERVER_EXCHANGES {
HTTP_REACTIVE_SERVER_REQUESTS {
@Override
public Class<? extends ObservationConvention<? extends Observation.Context>> getDefaultConvention() {
return DefaultServerRequestObservationConvention.class;

View File

@@ -14,46 +14,47 @@
* limitations under the License.
*/
package org.springframework.http.observation.reactive;
package org.springframework.http.server.reactive.observation;
import java.util.Collections;
import java.util.Map;
import io.micrometer.observation.transport.RequestReplyReceiverContext;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.lang.Nullable;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.pattern.PathPattern;
/**
* Context that holds information for metadata collection during observations
* for {@link ServerHttpObservationDocumentation#HTTP_REACTIVE_SERVER_EXCHANGES reactive HTTP exchanges}.
* Context that holds information for metadata collection regarding
* {@link ServerHttpObservationDocumentation#HTTP_REACTIVE_SERVER_REQUESTS reactive HTTP requests} observations.
* <p>This context also extends {@link RequestReplyReceiverContext} for propagating
* tracing information with the HTTP server exchange.
* tracing information during HTTP request processing.
*
* @author Brian Clozel
* @since 6.0
*/
public class ServerRequestObservationContext extends RequestReplyReceiverContext<ServerHttpRequest, ServerHttpResponse> {
private final ServerWebExchange serverWebExchange;
private final Map<String, Object> attributes;
@Nullable
private PathPattern pathPattern;
private String pathPattern;
private boolean connectionAborted;
public ServerRequestObservationContext(ServerWebExchange exchange) {
super((request, key) -> request.getHeaders().getFirst(key));
this.serverWebExchange = exchange;
setCarrier(exchange.getRequest());
setResponse(exchange.getResponse());
public ServerRequestObservationContext(ServerHttpRequest request, ServerHttpResponse response, Map<String, Object> attributes) {
super((req, key) -> req.getHeaders().getFirst(key));
setCarrier(request);
setResponse(response);
this.attributes = Collections.unmodifiableMap(attributes);
}
/**
* Return the current {@link ServerWebExchange HTTP exchange}.
* Return an immutable map of the current request attributes.
*/
public ServerWebExchange getServerWebExchange() {
return this.serverWebExchange;
public Map<String, Object> getAttributes() {
return this.attributes;
}
/**
@@ -63,7 +64,7 @@ public class ServerRequestObservationContext extends RequestReplyReceiverContext
* @return the path pattern, or {@code null} if none found
*/
@Nullable
public PathPattern getPathPattern() {
public String getPathPattern() {
return this.pathPattern;
}
@@ -72,7 +73,7 @@ public class ServerRequestObservationContext extends RequestReplyReceiverContext
* <p>Path patterns must have a low cardinality for the entire application.
* @param pathPattern the path pattern, for example {@code "/projects/{name}"}.
*/
public void setPathPattern(@Nullable PathPattern pathPattern) {
public void setPathPattern(@Nullable String pathPattern) {
this.pathPattern = pathPattern;
}

View File

@@ -14,13 +14,13 @@
* limitations under the License.
*/
package org.springframework.http.observation.reactive;
package org.springframework.http.server.reactive.observation;
import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationConvention;
/**
* Interface for an {@link ObservationConvention} for {@link ServerHttpObservationDocumentation#HTTP_REACTIVE_SERVER_EXCHANGES reactive HTTP exchanges}.
* Interface for an {@link ObservationConvention} for {@link ServerHttpObservationDocumentation#HTTP_REACTIVE_SERVER_REQUESTS reactive HTTP requests}.
*
* @author Brian Clozel
* @since 6.0

View File

@@ -1,9 +1,9 @@
/**
* Instrumentation for {@link io.micrometer.observation.Observation observing} reactive HTTP applications.
* Instrumentation for {@link io.micrometer.observation.Observation observing} reactive HTTP server applications.
*/
@NonNullApi
@NonNullFields
package org.springframework.http.observation.reactive;
package org.springframework.http.server.reactive.observation;
import org.springframework.lang.NonNullApi;
import org.springframework.lang.NonNullFields;

View File

@@ -27,10 +27,10 @@ import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.observation.DefaultServerRequestObservationConvention;
import org.springframework.http.observation.ServerHttpObservationDocumentation;
import org.springframework.http.observation.ServerRequestObservationContext;
import org.springframework.http.observation.ServerRequestObservationConvention;
import org.springframework.http.server.observation.DefaultServerRequestObservationConvention;
import org.springframework.http.server.observation.ServerHttpObservationDocumentation;
import org.springframework.http.server.observation.ServerRequestObservationContext;
import org.springframework.http.server.observation.ServerRequestObservationConvention;
import org.springframework.lang.Nullable;
@@ -127,7 +127,7 @@ public class ServerHttpObservationFilter extends OncePerRequestFilter {
Observation observation = (Observation) request.getAttribute(CURRENT_OBSERVATION_ATTRIBUTE);
if (observation == null) {
ServerRequestObservationContext context = new ServerRequestObservationContext(request, response);
observation = ServerHttpObservationDocumentation.HTTP_SERVLET_SERVER_EXCHANGES.observation(this.observationConvention,
observation = ServerHttpObservationDocumentation.HTTP_SERVLET_SERVER_REQUESTS.observation(this.observationConvention,
DEFAULT_OBSERVATION_CONVENTION, () -> context, this.observationRegistry).start();
request.setAttribute(CURRENT_OBSERVATION_ATTRIBUTE, observation);
if (!observation.isNoop()) {

View File

@@ -25,11 +25,11 @@ import io.micrometer.observation.contextpropagation.ObservationThreadLocalAccess
import org.reactivestreams.Publisher;
import reactor.core.publisher.Mono;
import org.springframework.http.observation.reactive.DefaultServerRequestObservationConvention;
import org.springframework.http.observation.reactive.ServerHttpObservationDocumentation;
import org.springframework.http.observation.reactive.ServerRequestObservationContext;
import org.springframework.http.observation.reactive.ServerRequestObservationConvention;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.observation.DefaultServerRequestObservationConvention;
import org.springframework.http.server.reactive.observation.ServerHttpObservationDocumentation;
import org.springframework.http.server.reactive.observation.ServerRequestObservationContext;
import org.springframework.http.server.reactive.observation.ServerRequestObservationConvention;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
@@ -96,13 +96,14 @@ public class ServerHttpObservationFilter implements WebFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
ServerRequestObservationContext observationContext = new ServerRequestObservationContext(exchange);
ServerRequestObservationContext observationContext = new ServerRequestObservationContext(exchange.getRequest(),
exchange.getResponse(), exchange.getAttributes());
exchange.getAttributes().put(CURRENT_OBSERVATION_CONTEXT_ATTRIBUTE, observationContext);
return chain.filter(exchange).transformDeferred(call -> filter(exchange, observationContext, call));
}
private Publisher<Void> filter(ServerWebExchange exchange, ServerRequestObservationContext observationContext, Mono<Void> call) {
Observation observation = ServerHttpObservationDocumentation.HTTP_REACTIVE_SERVER_EXCHANGES.observation(this.observationConvention,
Observation observation = ServerHttpObservationDocumentation.HTTP_REACTIVE_SERVER_REQUESTS.observation(this.observationConvention,
DEFAULT_OBSERVATION_CONVENTION, () -> observationContext, this.observationRegistry);
observation.start();
return call.doOnEach(signal -> {

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.http.observation;
package org.springframework.http.server.observation;
import io.micrometer.common.KeyValue;
import io.micrometer.observation.Observation;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.http.observation.reactive;
package org.springframework.http.server.reactive.observation;
import io.micrometer.common.KeyValue;
import io.micrometer.observation.Observation;
@@ -23,13 +23,12 @@ import org.junit.jupiter.api.Test;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.testfixture.http.server.reactive.MockServerHttpRequest;
import org.springframework.web.testfixture.server.MockServerWebExchange;
import org.springframework.web.util.pattern.PathPattern;
import org.springframework.web.util.pattern.PathPatternParser;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link DefaultServerRequestObservationConvention}.
*
* @author Brian Clozel
*/
class DefaultServerRequestObservationConventionTests {
@@ -45,22 +44,22 @@ class DefaultServerRequestObservationConventionTests {
@Test
void shouldHaveContextualName() {
ServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/test/resource"));
ServerRequestObservationContext context = new ServerRequestObservationContext(exchange);
ServerRequestObservationContext context = new ServerRequestObservationContext(exchange.getRequest(), exchange.getResponse(), exchange.getAttributes());
assertThat(convention.getContextualName(context)).isEqualTo("http get");
}
@Test
void contextualNameShouldUsePathPatternWhenAvailable() {
ServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/test/resource"));
ServerRequestObservationContext context = new ServerRequestObservationContext(exchange);
context.setPathPattern(PathPatternParser.defaultInstance.parse("/test/{name}"));
ServerRequestObservationContext context = new ServerRequestObservationContext(exchange.getRequest(), exchange.getResponse(), exchange.getAttributes());
context.setPathPattern("/test/{name}");
assertThat(convention.getContextualName(context)).isEqualTo("http get /test/{name}");
}
@Test
void supportsOnlyHttpRequestsObservationContext() {
ServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.post("/test/resource"));
ServerRequestObservationContext context = new ServerRequestObservationContext(exchange);
ServerRequestObservationContext context = new ServerRequestObservationContext(exchange.getRequest(), exchange.getResponse(), exchange.getAttributes());
assertThat(this.convention.supportsContext(context)).isTrue();
assertThat(this.convention.supportsContext(new Observation.Context())).isFalse();
}
@@ -69,7 +68,7 @@ class DefaultServerRequestObservationConventionTests {
void addsKeyValuesForExchange() {
ServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.post("/test/resource"));
exchange.getResponse().setRawStatusCode(201);
ServerRequestObservationContext context = new ServerRequestObservationContext(exchange);
ServerRequestObservationContext context = new ServerRequestObservationContext(exchange.getRequest(), exchange.getResponse(), exchange.getAttributes());
assertThat(this.convention.getLowCardinalityKeyValues(context)).hasSize(5)
.contains(KeyValue.of("method", "POST"), KeyValue.of("uri", "UNKNOWN"), KeyValue.of("status", "201"),
@@ -82,9 +81,8 @@ class DefaultServerRequestObservationConventionTests {
void addsKeyValuesForExchangeWithPathPattern() {
ServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/test/resource"));
exchange.getResponse().setRawStatusCode(200);
ServerRequestObservationContext context = new ServerRequestObservationContext(exchange);
PathPattern pathPattern = getPathPattern("/test/{name}");
context.setPathPattern(pathPattern);
ServerRequestObservationContext context = new ServerRequestObservationContext(exchange.getRequest(), exchange.getResponse(), exchange.getAttributes());
context.setPathPattern("/test/{name}");
assertThat(this.convention.getLowCardinalityKeyValues(context)).hasSize(5)
.contains(KeyValue.of("method", "GET"), KeyValue.of("uri", "/test/{name}"), KeyValue.of("status", "200"),
@@ -97,7 +95,7 @@ class DefaultServerRequestObservationConventionTests {
@Test
void addsKeyValuesForErrorExchange() {
ServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/test/resource"));
ServerRequestObservationContext context = new ServerRequestObservationContext(exchange);
ServerRequestObservationContext context = new ServerRequestObservationContext(exchange.getRequest(), exchange.getResponse(), exchange.getAttributes());
context.setError(new IllegalArgumentException("custom error"));
exchange.getResponse().setRawStatusCode(500);
@@ -111,7 +109,7 @@ class DefaultServerRequestObservationConventionTests {
@Test
void addsKeyValuesForRedirectExchange() {
ServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/test/redirect"));
ServerRequestObservationContext context = new ServerRequestObservationContext(exchange);
ServerRequestObservationContext context = new ServerRequestObservationContext(exchange.getRequest(), exchange.getResponse(), exchange.getAttributes());
exchange.getResponse().setRawStatusCode(302);
exchange.getResponse().getHeaders().add("Location", "https://example.org/other");
@@ -125,7 +123,7 @@ class DefaultServerRequestObservationConventionTests {
@Test
void addsKeyValuesForNotFoundExchange() {
ServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/test/notFound"));
ServerRequestObservationContext context = new ServerRequestObservationContext(exchange);
ServerRequestObservationContext context = new ServerRequestObservationContext(exchange.getRequest(), exchange.getResponse(), exchange.getAttributes());
exchange.getResponse().setRawStatusCode(404);
assertThat(this.convention.getLowCardinalityKeyValues(context)).hasSize(5)
@@ -138,7 +136,7 @@ class DefaultServerRequestObservationConventionTests {
@Test
void addsKeyValuesForCancelledExchange() {
ServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/test/resource"));
ServerRequestObservationContext context = new ServerRequestObservationContext(exchange);
ServerRequestObservationContext context = new ServerRequestObservationContext(exchange.getRequest(), exchange.getResponse(), exchange.getAttributes());
context.setConnectionAborted(true);
exchange.getResponse().setRawStatusCode(200);
@@ -152,16 +150,11 @@ class DefaultServerRequestObservationConventionTests {
@Test
void supportsNullStatusCode() {
ServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/test/resource"));
ServerRequestObservationContext context = new ServerRequestObservationContext(exchange);
ServerRequestObservationContext context = new ServerRequestObservationContext(exchange.getRequest(), exchange.getResponse(), exchange.getAttributes());
assertThat(this.convention.getLowCardinalityKeyValues(context))
.contains(KeyValue.of("status", "UNKNOWN"),
KeyValue.of("exception", "none"), KeyValue.of("outcome", "UNKNOWN"));
}
private static PathPattern getPathPattern(String pattern) {
PathPatternParser pathPatternParser = new PathPatternParser();
return pathPatternParser.parse(pattern);
}
}

View File

@@ -24,7 +24,7 @@ import jakarta.servlet.ServletException;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpMethod;
import org.springframework.http.observation.ServerRequestObservationContext;
import org.springframework.http.server.observation.ServerRequestObservationContext;
import org.springframework.web.testfixture.servlet.MockFilterChain;
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
import org.springframework.web.testfixture.servlet.MockHttpServletResponse;

View File

@@ -27,7 +27,7 @@ import org.junit.jupiter.api.Test;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import org.springframework.http.observation.reactive.ServerRequestObservationContext;
import org.springframework.http.server.reactive.observation.ServerRequestObservationContext;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilterChain;
import org.springframework.web.testfixture.http.server.reactive.MockServerHttpRequest;

View File

@@ -172,7 +172,7 @@ public class RouterFunctionMapping extends AbstractHandlerMapping implements Ini
if (matchingPattern != null) {
attributes.put(BEST_MATCHING_PATTERN_ATTRIBUTE, matchingPattern);
ServerHttpObservationFilter.findObservationContext(serverRequest.exchange())
.ifPresent(context -> context.setPathPattern(matchingPattern));
.ifPresent(context -> context.setPathPattern(matchingPattern.toString()));
}
Map<String, String> uriVariables =
(Map<String, String>) attributes.get(RouterFunctions.URI_TEMPLATE_VARIABLES_ATTRIBUTE);

View File

@@ -167,7 +167,7 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
exchange.getAttributes().put(BEST_MATCHING_HANDLER_ATTRIBUTE, handler);
exchange.getAttributes().put(BEST_MATCHING_PATTERN_ATTRIBUTE, pattern);
ServerHttpObservationFilter.findObservationContext(exchange)
.ifPresent(context -> context.setPathPattern(pattern));
.ifPresent(context -> context.setPathPattern(pattern.toString()));
exchange.getAttributes().put(PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, pathWithinMapping);
exchange.getAttributes().put(URI_TEMPLATE_VARIABLES_ATTRIBUTE, matchInfo.getUriVariables());

View File

@@ -141,7 +141,7 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
exchange.getAttributes().put(BEST_MATCHING_HANDLER_ATTRIBUTE, handlerMethod);
exchange.getAttributes().put(BEST_MATCHING_PATTERN_ATTRIBUTE, bestPattern);
ServerHttpObservationFilter.findObservationContext(exchange)
.ifPresent(context -> context.setPathPattern(bestPattern));
.ifPresent(context -> context.setPathPattern(bestPattern.toString()));
exchange.getAttributes().put(URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriVariables);
exchange.getAttributes().put(MATRIX_VARIABLES_ATTRIBUTE, matrixVariables);

View File

@@ -22,7 +22,7 @@ import reactor.test.StepVerifier;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.http.observation.reactive.ServerRequestObservationContext;
import org.springframework.http.server.reactive.observation.ServerRequestObservationContext;
import org.springframework.web.filter.reactive.ServerHttpObservationFilter;
import org.springframework.web.reactive.HandlerMapping;
import org.springframework.web.reactive.function.server.HandlerFunction;
@@ -137,7 +137,7 @@ class RouterFunctionMappingTests {
assertThat(matchingPattern).isNotNull();
assertThat(matchingPattern.getPatternString()).isEqualTo("/match");
assertThat(ServerHttpObservationFilter.findObservationContext(exchange))
.hasValueSatisfying(context -> assertThat(context.getPathPattern()).isEqualTo(matchingPattern));
.hasValueSatisfying(context -> assertThat(context.getPathPattern()).isEqualTo(matchingPattern.getPatternString()));
ServerRequest serverRequest = exchange.getAttribute(RouterFunctions.REQUEST_ATTRIBUTE);
assertThat(serverRequest).isNotNull();
@@ -148,7 +148,7 @@ class RouterFunctionMappingTests {
private ServerWebExchange createExchange(String urlTemplate) {
MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(urlTemplate));
ServerRequestObservationContext observationContext = new ServerRequestObservationContext(exchange);
ServerRequestObservationContext observationContext = new ServerRequestObservationContext(exchange.getRequest(), exchange.getResponse(), exchange.getAttributes());
exchange.getAttributes().put(CURRENT_OBSERVATION_CONTEXT_ATTRIBUTE, observationContext);
return exchange;
}

View File

@@ -36,7 +36,7 @@ import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.observation.reactive.ServerRequestObservationContext;
import org.springframework.http.server.reactive.observation.ServerRequestObservationContext;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Controller;
import org.springframework.util.ClassUtils;
@@ -263,12 +263,11 @@ public class RequestMappingInfoHandlerMappingTests {
public void handleMatchBestMatchingPatternAttributeInObservationContext() {
RequestMappingInfo key = paths("/{path1}/2", "/**").build();
ServerWebExchange exchange = MockServerWebExchange.from(get("/1/2"));
ServerRequestObservationContext observationContext = new ServerRequestObservationContext(exchange);
ServerRequestObservationContext observationContext = new ServerRequestObservationContext(exchange.getRequest(), exchange.getResponse(), exchange.getAttributes());
exchange.getAttributes().put(CURRENT_OBSERVATION_CONTEXT_ATTRIBUTE, observationContext);
this.handlerMapping.handleMatch(key, handlerMethod, exchange);
assertThat(observationContext.getPathPattern()).isNotNull();
assertThat(observationContext.getPathPattern().toString()).isEqualTo("/{path1}/2");
assertThat(observationContext.getPathPattern()).isEqualTo("/{path1}/2");
}
@Test // gh-22543

View File

@@ -26,7 +26,7 @@ import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.observation.ServerRequestObservationContext;
import org.springframework.http.server.observation.ServerRequestObservationContext;
import org.springframework.web.filter.ServerHttpObservationFilter;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.HandlerMapping;

View File

@@ -33,8 +33,8 @@ import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.observation.ServerRequestObservationContext;
import org.springframework.http.server.RequestPath;
import org.springframework.http.server.observation.ServerRequestObservationContext;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Controller;
import org.springframework.util.MultiValueMap;