Browse Source

Set matching pattern in reactive server observation context

This commit fixes the observation instrumentation for the reactive HTTP
server by setting the best matching pattern determined by the web
framework into the `ServerRequestObservationContext`.

This information is required by the observation convention for creating
the expected `KeyValue` for the matching pattern. Prior to this commit,
the information was missing and resulted in an UNKNOWN key value.

Fixes gh-29422
pull/29167/head
Brian Clozel 3 years ago
parent
commit
db79d1d2b9
  1. 3
      spring-webflux/src/main/java/org/springframework/web/reactive/function/server/support/RouterFunctionMapping.java
  2. 3
      spring-webflux/src/main/java/org/springframework/web/reactive/handler/AbstractUrlHandlerMapping.java
  3. 3
      spring-webflux/src/main/java/org/springframework/web/reactive/result/method/RequestMappingInfoHandlerMapping.java
  4. 11
      spring-webflux/src/test/java/org/springframework/web/reactive/function/server/support/RouterFunctionMappingTests.java
  5. 14
      spring-webflux/src/test/java/org/springframework/web/reactive/result/method/RequestMappingInfoHandlerMappingTests.java

3
spring-webflux/src/main/java/org/springframework/web/reactive/function/server/support/RouterFunctionMapping.java

@ -28,6 +28,7 @@ import org.springframework.http.codec.HttpMessageReader;
import org.springframework.http.codec.ServerCodecConfigurer; import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import org.springframework.web.filter.reactive.ServerHttpObservationFilter;
import org.springframework.web.reactive.function.server.HandlerFunction; import org.springframework.web.reactive.function.server.HandlerFunction;
import org.springframework.web.reactive.function.server.RouterFunction; import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions; import org.springframework.web.reactive.function.server.RouterFunctions;
@ -170,6 +171,8 @@ public class RouterFunctionMapping extends AbstractHandlerMapping implements Ini
PathPattern matchingPattern = (PathPattern) attributes.get(RouterFunctions.MATCHING_PATTERN_ATTRIBUTE); PathPattern matchingPattern = (PathPattern) attributes.get(RouterFunctions.MATCHING_PATTERN_ATTRIBUTE);
if (matchingPattern != null) { if (matchingPattern != null) {
attributes.put(BEST_MATCHING_PATTERN_ATTRIBUTE, matchingPattern); attributes.put(BEST_MATCHING_PATTERN_ATTRIBUTE, matchingPattern);
ServerHttpObservationFilter.findObservationContext(serverRequest.exchange())
.ifPresent(context -> context.setPathPattern(matchingPattern));
} }
Map<String, String> uriVariables = Map<String, String> uriVariables =
(Map<String, String>) attributes.get(RouterFunctions.URI_TEMPLATE_VARIABLES_ATTRIBUTE); (Map<String, String>) attributes.get(RouterFunctions.URI_TEMPLATE_VARIABLES_ATTRIBUTE);

3
spring-webflux/src/main/java/org/springframework/web/reactive/handler/AbstractUrlHandlerMapping.java

@ -30,6 +30,7 @@ import org.springframework.http.server.PathContainer;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.web.filter.reactive.ServerHttpObservationFilter;
import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.pattern.PathPattern; import org.springframework.web.util.pattern.PathPattern;
@ -165,6 +166,8 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
exchange.getAttributes().put(BEST_MATCHING_HANDLER_ATTRIBUTE, handler); exchange.getAttributes().put(BEST_MATCHING_HANDLER_ATTRIBUTE, handler);
exchange.getAttributes().put(BEST_MATCHING_PATTERN_ATTRIBUTE, pattern); exchange.getAttributes().put(BEST_MATCHING_PATTERN_ATTRIBUTE, pattern);
ServerHttpObservationFilter.findObservationContext(exchange)
.ifPresent(context -> context.setPathPattern(pattern));
exchange.getAttributes().put(PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, pathWithinMapping); exchange.getAttributes().put(PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, pathWithinMapping);
exchange.getAttributes().put(URI_TEMPLATE_VARIABLES_ATTRIBUTE, matchInfo.getUriVariables()); exchange.getAttributes().put(URI_TEMPLATE_VARIABLES_ATTRIBUTE, matchInfo.getUriVariables());

3
spring-webflux/src/main/java/org/springframework/web/reactive/result/method/RequestMappingInfoHandlerMapping.java

@ -38,6 +38,7 @@ import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.filter.reactive.ServerHttpObservationFilter;
import org.springframework.web.method.HandlerMethod; import org.springframework.web.method.HandlerMethod;
import org.springframework.web.reactive.HandlerMapping; import org.springframework.web.reactive.HandlerMapping;
import org.springframework.web.reactive.result.condition.NameValueExpression; import org.springframework.web.reactive.result.condition.NameValueExpression;
@ -139,6 +140,8 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
exchange.getAttributes().put(BEST_MATCHING_HANDLER_ATTRIBUTE, handlerMethod); exchange.getAttributes().put(BEST_MATCHING_HANDLER_ATTRIBUTE, handlerMethod);
exchange.getAttributes().put(BEST_MATCHING_PATTERN_ATTRIBUTE, bestPattern); exchange.getAttributes().put(BEST_MATCHING_PATTERN_ATTRIBUTE, bestPattern);
ServerHttpObservationFilter.findObservationContext(exchange)
.ifPresent(context -> context.setPathPattern(bestPattern));
exchange.getAttributes().put(URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriVariables); exchange.getAttributes().put(URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriVariables);
exchange.getAttributes().put(MATRIX_VARIABLES_ATTRIBUTE, matrixVariables); exchange.getAttributes().put(MATRIX_VARIABLES_ATTRIBUTE, matrixVariables);

11
spring-webflux/src/test/java/org/springframework/web/reactive/function/server/support/RouterFunctionMappingTests.java

@ -22,6 +22,8 @@ import reactor.test.StepVerifier;
import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.http.codec.ServerCodecConfigurer; import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.http.observation.reactive.ServerRequestObservationContext;
import org.springframework.web.filter.reactive.ServerHttpObservationFilter;
import org.springframework.web.reactive.HandlerMapping; import org.springframework.web.reactive.HandlerMapping;
import org.springframework.web.reactive.function.server.HandlerFunction; import org.springframework.web.reactive.function.server.HandlerFunction;
import org.springframework.web.reactive.function.server.RouterFunction; import org.springframework.web.reactive.function.server.RouterFunction;
@ -34,8 +36,10 @@ import org.springframework.web.testfixture.server.MockServerWebExchange;
import org.springframework.web.util.pattern.PathPattern; import org.springframework.web.util.pattern.PathPattern;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.web.filter.reactive.ServerHttpObservationFilter.CURRENT_OBSERVATION_CONTEXT_ATTRIBUTE;
/** /**
* Tests for {@link RouterFunctionMapping}.
* @author Arjen Poutsma * @author Arjen Poutsma
* @author Brian Clozel * @author Brian Clozel
*/ */
@ -132,6 +136,8 @@ class RouterFunctionMappingTests {
PathPattern matchingPattern = exchange.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE); PathPattern matchingPattern = exchange.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
assertThat(matchingPattern).isNotNull(); assertThat(matchingPattern).isNotNull();
assertThat(matchingPattern.getPatternString()).isEqualTo("/match"); assertThat(matchingPattern.getPatternString()).isEqualTo("/match");
assertThat(ServerHttpObservationFilter.findObservationContext(exchange))
.hasValueSatisfying(context -> assertThat(context.getPathPattern()).isEqualTo(matchingPattern));
ServerRequest serverRequest = exchange.getAttribute(RouterFunctions.REQUEST_ATTRIBUTE); ServerRequest serverRequest = exchange.getAttribute(RouterFunctions.REQUEST_ATTRIBUTE);
assertThat(serverRequest).isNotNull(); assertThat(serverRequest).isNotNull();
@ -141,7 +147,10 @@ class RouterFunctionMappingTests {
} }
private ServerWebExchange createExchange(String urlTemplate) { private ServerWebExchange createExchange(String urlTemplate) {
return MockServerWebExchange.from(MockServerHttpRequest.get(urlTemplate)); MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(urlTemplate));
ServerRequestObservationContext observationContext = new ServerRequestObservationContext(exchange);
exchange.getAttributes().put(CURRENT_OBSERVATION_CONTEXT_ATTRIBUTE, observationContext);
return exchange;
} }
} }

14
spring-webflux/src/test/java/org/springframework/web/reactive/result/method/RequestMappingInfoHandlerMappingTests.java

@ -36,6 +36,7 @@ import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.observation.reactive.ServerRequestObservationContext;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
@ -63,6 +64,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.web.bind.annotation.RequestMethod.GET; import static org.springframework.web.bind.annotation.RequestMethod.GET;
import static org.springframework.web.bind.annotation.RequestMethod.HEAD; import static org.springframework.web.bind.annotation.RequestMethod.HEAD;
import static org.springframework.web.bind.annotation.RequestMethod.OPTIONS; import static org.springframework.web.bind.annotation.RequestMethod.OPTIONS;
import static org.springframework.web.filter.reactive.ServerHttpObservationFilter.CURRENT_OBSERVATION_CONTEXT_ATTRIBUTE;
import static org.springframework.web.reactive.HandlerMapping.BEST_MATCHING_HANDLER_ATTRIBUTE; import static org.springframework.web.reactive.HandlerMapping.BEST_MATCHING_HANDLER_ATTRIBUTE;
import static org.springframework.web.reactive.HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE; import static org.springframework.web.reactive.HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE;
import static org.springframework.web.reactive.result.method.RequestMappingInfo.paths; import static org.springframework.web.reactive.result.method.RequestMappingInfo.paths;
@ -257,6 +259,18 @@ public class RequestMappingInfoHandlerMappingTests {
assertThat(mapped).isSameAs(handlerMethod); assertThat(mapped).isSameAs(handlerMethod);
} }
@Test
public void handleMatchBestMatchingPatternAttributeInObservationContext() {
RequestMappingInfo key = paths("/{path1}/2", "/**").build();
ServerWebExchange exchange = MockServerWebExchange.from(get("/1/2"));
ServerRequestObservationContext observationContext = new ServerRequestObservationContext(exchange);
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");
}
@Test // gh-22543 @Test // gh-22543
public void handleMatchBestMatchingPatternAttributeNoPatternsDefined() { public void handleMatchBestMatchingPatternAttributeNoPatternsDefined() {
ServerWebExchange exchange = MockServerWebExchange.from(get("")); ServerWebExchange exchange = MockServerWebExchange.from(get(""));

Loading…
Cancel
Save