Improved logging in functional web framework
This commit improves predicate and route logging in the functional web framework.
This commit is contained in:
		
							parent
							
								
									63f261155a
								
							
						
					
					
						commit
						9ab8bd046c
					
				| 
						 | 
				
			
			@ -127,6 +127,11 @@ class DefaultServerRequest implements ServerRequest {
 | 
			
		|||
		return this.exchange.getAttribute(name);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Map<String, Object> attributes() {
 | 
			
		||||
		return this.exchange.getAttributes();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public List<String> queryParams(String name) {
 | 
			
		||||
		List<String> queryParams = request().getQueryParams().get(name);
 | 
			
		||||
| 
						 | 
				
			
			@ -152,6 +157,10 @@ class DefaultServerRequest implements ServerRequest {
 | 
			
		|||
		return this.exchange;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public String toString() {
 | 
			
		||||
		return String.format("%s %s", method(), path());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private class DefaultHeaders implements Headers {
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -205,6 +214,11 @@ class DefaultServerRequest implements ServerRequest {
 | 
			
		|||
		public HttpHeaders asHttpHeaders() {
 | 
			
		||||
			return HttpHeaders.readOnlyHttpHeaders(delegate());
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@Override
 | 
			
		||||
		public String toString() {
 | 
			
		||||
			return delegate().toString();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -57,8 +57,13 @@ public interface RequestPredicate {
 | 
			
		|||
			}
 | 
			
		||||
 | 
			
		||||
			@Override
 | 
			
		||||
			public ServerRequest subRequest(ServerRequest request) {
 | 
			
		||||
				return other.subRequest(RequestPredicate.this.subRequest(request));
 | 
			
		||||
			public ServerRequest nestRequest(ServerRequest request) {
 | 
			
		||||
				return other.nestRequest(RequestPredicate.this.nestRequest(request));
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			@Override
 | 
			
		||||
			public String toString() {
 | 
			
		||||
				return String.format("(%s && %s)", RequestPredicate.this, other);
 | 
			
		||||
			}
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -76,16 +81,47 @@ public interface RequestPredicate {
 | 
			
		|||
	 * Returns a composed request predicate that tests against both this predicate OR the {@code other} predicate.
 | 
			
		||||
	 * When evaluating the composed predicate, if this predicate is {@code true}, then the {@code other} predicate
 | 
			
		||||
	 * is not evaluated.
 | 
			
		||||
 | 
			
		||||
	 * @param other a predicate that will be logically-ORed with this predicate
 | 
			
		||||
	 * @return a predicate composed of this predicate OR the {@code other} predicate
 | 
			
		||||
	 */
 | 
			
		||||
	default RequestPredicate or(RequestPredicate other) {
 | 
			
		||||
		Assert.notNull(other, "'other' must not be null");
 | 
			
		||||
		return (t) -> test(t) || other.test(t);
 | 
			
		||||
		return new RequestPredicate() {
 | 
			
		||||
			@Override
 | 
			
		||||
			public boolean test(ServerRequest t) {
 | 
			
		||||
				return RequestPredicate.this.test(t) || other.test(t);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
	default ServerRequest subRequest(ServerRequest request) {
 | 
			
		||||
			@Override
 | 
			
		||||
			public ServerRequest nestRequest(ServerRequest request) {
 | 
			
		||||
				if (RequestPredicate.this.test(request)) {
 | 
			
		||||
					return RequestPredicate.this.nestRequest(request);
 | 
			
		||||
				}
 | 
			
		||||
				else if (other.test(request)) {
 | 
			
		||||
					return other.nestRequest(request);
 | 
			
		||||
				}
 | 
			
		||||
				else {
 | 
			
		||||
					throw new IllegalStateException("Neither " + RequestPredicate.this.toString() +
 | 
			
		||||
							" nor " + other + "matches");
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			@Override
 | 
			
		||||
			public String toString() {
 | 
			
		||||
				return String.format("(%s || %s)", RequestPredicate.this, other);
 | 
			
		||||
			}
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Transforms the given request into a request used for a nested route. For instance, a
 | 
			
		||||
	 * path-based predicate can return a {@code ServerRequest} with a nested path.
 | 
			
		||||
	 * <p>Default implementation returns the given path.
 | 
			
		||||
	 * @param request the request to be nested
 | 
			
		||||
	 * @return the nested request
 | 
			
		||||
	 * @see RouterFunctions#nest(RequestPredicate, RouterFunction)
 | 
			
		||||
	 */
 | 
			
		||||
	default ServerRequest nestRequest(ServerRequest request) {
 | 
			
		||||
		return request;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,6 +26,8 @@ import java.util.Set;
 | 
			
		|||
import java.util.function.Function;
 | 
			
		||||
import java.util.function.Predicate;
 | 
			
		||||
 | 
			
		||||
import org.apache.commons.logging.Log;
 | 
			
		||||
import org.apache.commons.logging.LogFactory;
 | 
			
		||||
import reactor.core.publisher.Flux;
 | 
			
		||||
import reactor.core.publisher.Mono;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -33,6 +35,7 @@ import org.springframework.http.HttpMethod;
 | 
			
		|||
import org.springframework.http.MediaType;
 | 
			
		||||
import org.springframework.http.server.reactive.ServerHttpRequest;
 | 
			
		||||
import org.springframework.util.Assert;
 | 
			
		||||
import org.springframework.util.StringUtils;
 | 
			
		||||
import org.springframework.web.reactive.function.BodyExtractor;
 | 
			
		||||
import org.springframework.web.server.WebSession;
 | 
			
		||||
import org.springframework.web.util.UriUtils;
 | 
			
		||||
| 
						 | 
				
			
			@ -48,6 +51,8 @@ import org.springframework.web.util.patterns.PathPatternParser;
 | 
			
		|||
 */
 | 
			
		||||
public abstract class RequestPredicates {
 | 
			
		||||
 | 
			
		||||
	private static final Log logger = LogFactory.getLog(RequestPredicates.class);
 | 
			
		||||
 | 
			
		||||
	private static final PathPatternParser DEFAULT_PATTERN_PARSER = new PathPatternParser();
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			@ -119,11 +124,21 @@ public abstract class RequestPredicates {
 | 
			
		|||
	public static RequestPredicate contentType(MediaType... mediaTypes) {
 | 
			
		||||
		Assert.notEmpty(mediaTypes, "'mediaTypes' must not be empty");
 | 
			
		||||
		Set<MediaType> mediaTypeSet = new HashSet<>(Arrays.asList(mediaTypes));
 | 
			
		||||
		return headers(headers -> {
 | 
			
		||||
			MediaType contentType = headers.contentType().orElse(MediaType.APPLICATION_OCTET_STREAM);
 | 
			
		||||
			return mediaTypeSet.stream()
 | 
			
		||||
		return headers(new Predicate<ServerRequest.Headers>() {
 | 
			
		||||
			@Override
 | 
			
		||||
			public boolean test(ServerRequest.Headers headers) {
 | 
			
		||||
				MediaType contentType =
 | 
			
		||||
						headers.contentType().orElse(MediaType.APPLICATION_OCTET_STREAM);
 | 
			
		||||
				boolean match = mediaTypeSet.stream()
 | 
			
		||||
						.anyMatch(mediaType -> mediaType.includes(contentType));
 | 
			
		||||
				traceMatch("Content-Type", mediaTypeSet, contentType, match);
 | 
			
		||||
				return match;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			@Override
 | 
			
		||||
			public String toString() {
 | 
			
		||||
				return String.format("Content-Type: %s", mediaTypeSet);
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -138,12 +153,23 @@ public abstract class RequestPredicates {
 | 
			
		|||
	public static RequestPredicate accept(MediaType... mediaTypes) {
 | 
			
		||||
		Assert.notEmpty(mediaTypes, "'mediaTypes' must not be empty");
 | 
			
		||||
		Set<MediaType> mediaTypeSet = new HashSet<>(Arrays.asList(mediaTypes));
 | 
			
		||||
		return headers(headers -> {
 | 
			
		||||
		return headers(new Predicate<ServerRequest.Headers>() {
 | 
			
		||||
			@Override
 | 
			
		||||
			public boolean test(ServerRequest.Headers headers) {
 | 
			
		||||
				List<MediaType> acceptedMediaTypes = headers.accept();
 | 
			
		||||
				MediaType.sortBySpecificityAndQuality(acceptedMediaTypes);
 | 
			
		||||
			return acceptedMediaTypes.stream()
 | 
			
		||||
				boolean match = acceptedMediaTypes.stream()
 | 
			
		||||
						.anyMatch(acceptedMediaType -> mediaTypeSet.stream()
 | 
			
		||||
								.anyMatch(acceptedMediaType::isCompatibleWith));
 | 
			
		||||
				traceMatch("Accept", mediaTypeSet, acceptedMediaTypes, match);
 | 
			
		||||
				return match;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			@Override
 | 
			
		||||
			public String toString() {
 | 
			
		||||
				return String.format("Accept: %s", mediaTypeSet);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -238,7 +264,11 @@ public abstract class RequestPredicates {
 | 
			
		|||
	 */
 | 
			
		||||
	public static RequestPredicate pathExtension(String extension) {
 | 
			
		||||
		Assert.notNull(extension, "'extension' must not be null");
 | 
			
		||||
		return pathExtension(extension::equalsIgnoreCase);
 | 
			
		||||
		return pathExtension(pathExtension -> {
 | 
			
		||||
			boolean match = extension.equalsIgnoreCase(pathExtension);
 | 
			
		||||
			traceMatch("Extension", extension, pathExtension, match);
 | 
			
		||||
			return match;
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			@ -289,6 +319,14 @@ public abstract class RequestPredicates {
 | 
			
		|||
		};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private static void traceMatch(String prefix, Object desired, Object actual, boolean match) {
 | 
			
		||||
		if (logger.isTraceEnabled()) {
 | 
			
		||||
			String message = String.format("%s \"%s\" %s against value \"%s\"",
 | 
			
		||||
					prefix, desired, match ? "matches" : "does not match", actual);
 | 
			
		||||
			logger.trace(message);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	private static class HttpMethodPredicate implements RequestPredicate {
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -301,7 +339,14 @@ public abstract class RequestPredicates {
 | 
			
		|||
 | 
			
		||||
		@Override
 | 
			
		||||
		public boolean test(ServerRequest request) {
 | 
			
		||||
			return this.httpMethod == request.method();
 | 
			
		||||
			boolean match = this.httpMethod == request.method();
 | 
			
		||||
			traceMatch("Method", this.httpMethod, request.method(), match);
 | 
			
		||||
			return match;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@Override
 | 
			
		||||
		public String toString() {
 | 
			
		||||
			return this.httpMethod.toString();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -317,12 +362,11 @@ public abstract class RequestPredicates {
 | 
			
		|||
		@Override
 | 
			
		||||
		public boolean test(ServerRequest request) {
 | 
			
		||||
			String path = request.path();
 | 
			
		||||
			if (this.pattern.matches(path)) {
 | 
			
		||||
				if (request instanceof DefaultServerRequest) {
 | 
			
		||||
					DefaultServerRequest defaultRequest = (DefaultServerRequest) request;
 | 
			
		||||
			boolean match = this.pattern.matches(path);
 | 
			
		||||
			traceMatch("Pattern", this.pattern.getPatternString(), path, match);
 | 
			
		||||
			if (match) {
 | 
			
		||||
				Map<String, String> uriTemplateVariables = this.pattern.matchAndExtract(path);
 | 
			
		||||
					defaultRequest.exchange().getAttributes().put(RouterFunctions.URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriTemplateVariables);
 | 
			
		||||
				}
 | 
			
		||||
				request.attributes().put(RouterFunctions.URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriTemplateVariables);
 | 
			
		||||
				return true;
 | 
			
		||||
			}
 | 
			
		||||
			else {
 | 
			
		||||
| 
						 | 
				
			
			@ -331,11 +375,19 @@ public abstract class RequestPredicates {
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
		@Override
 | 
			
		||||
		public ServerRequest subRequest(ServerRequest request) {
 | 
			
		||||
		public ServerRequest nestRequest(ServerRequest request) {
 | 
			
		||||
			String requestPath = request.path();
 | 
			
		||||
			String subPath = this.pattern.extractPathWithinPattern(requestPath);
 | 
			
		||||
			if (!subPath.startsWith("/")) {
 | 
			
		||||
				subPath = "/" + subPath;
 | 
			
		||||
			}
 | 
			
		||||
			return new SubPathServerRequestWrapper(request, subPath);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@Override
 | 
			
		||||
		public String toString() {
 | 
			
		||||
			return this.pattern.getPatternString();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private static class HeadersPredicate implements RequestPredicate {
 | 
			
		||||
| 
						 | 
				
			
			@ -351,6 +403,11 @@ public abstract class RequestPredicates {
 | 
			
		|||
		public boolean test(ServerRequest request) {
 | 
			
		||||
			return this.headersPredicate.test(request.headers());
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@Override
 | 
			
		||||
		public String toString() {
 | 
			
		||||
			return this.headersPredicate.toString();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private static class SubPathServerRequestWrapper implements ServerRequest {
 | 
			
		||||
| 
						 | 
				
			
			@ -409,6 +466,11 @@ public abstract class RequestPredicates {
 | 
			
		|||
			return this.request.attribute(name);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@Override
 | 
			
		||||
		public Map<String, Object> attributes() {
 | 
			
		||||
			return this.request.attributes();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@Override
 | 
			
		||||
		public Optional<String> queryParam(String name) {
 | 
			
		||||
			return this.request.queryParam(name);
 | 
			
		||||
| 
						 | 
				
			
			@ -433,5 +495,11 @@ public abstract class RequestPredicates {
 | 
			
		|||
		public Mono<WebSession> session() {
 | 
			
		||||
			return this.request.session();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@Override
 | 
			
		||||
		public String toString() {
 | 
			
		||||
			return String.format("%s %s", method(), path());
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,6 +19,8 @@ package org.springframework.web.reactive.function.server;
 | 
			
		|||
import java.util.Map;
 | 
			
		||||
import java.util.function.Function;
 | 
			
		||||
 | 
			
		||||
import org.apache.commons.logging.Log;
 | 
			
		||||
import org.apache.commons.logging.LogFactory;
 | 
			
		||||
import reactor.core.publisher.Mono;
 | 
			
		||||
 | 
			
		||||
import org.springframework.core.io.Resource;
 | 
			
		||||
| 
						 | 
				
			
			@ -52,6 +54,8 @@ import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
 | 
			
		|||
 */
 | 
			
		||||
public abstract class RouterFunctions {
 | 
			
		||||
 | 
			
		||||
	private static final Log logger = LogFactory.getLog(RouterFunctions.class);
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Name of the {@link ServerWebExchange} attribute that contains the {@link ServerRequest}.
 | 
			
		||||
	 */
 | 
			
		||||
| 
						 | 
				
			
			@ -90,7 +94,18 @@ public abstract class RouterFunctions {
 | 
			
		|||
		Assert.notNull(predicate, "'predicate' must not be null");
 | 
			
		||||
		Assert.notNull(handlerFunction, "'handlerFunction' must not be null");
 | 
			
		||||
 | 
			
		||||
		return request -> predicate.test(request) ? Mono.just(handlerFunction) : Mono.empty();
 | 
			
		||||
		return request -> {
 | 
			
		||||
			if (predicate.test(request)) {
 | 
			
		||||
				if (logger.isDebugEnabled()) {
 | 
			
		||||
					logger.debug(String.format("Predicate \"%s\" matches against \"%s\"",
 | 
			
		||||
							predicate, request));
 | 
			
		||||
				}
 | 
			
		||||
				return Mono.just(handlerFunction);
 | 
			
		||||
			}
 | 
			
		||||
			else {
 | 
			
		||||
				return Mono.empty();
 | 
			
		||||
			}
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			@ -124,7 +139,11 @@ public abstract class RouterFunctions {
 | 
			
		|||
 | 
			
		||||
		return request -> {
 | 
			
		||||
			if (predicate.test(request)) {
 | 
			
		||||
				ServerRequest subRequest = predicate.subRequest(request);
 | 
			
		||||
				if (logger.isDebugEnabled()) {
 | 
			
		||||
					logger.debug(String.format("Nested predicate \"%s\" matches against \"%s\"",
 | 
			
		||||
							predicate, request));
 | 
			
		||||
				}
 | 
			
		||||
				ServerRequest subRequest = predicate.nestRequest(request);
 | 
			
		||||
				return routerFunction.route(subRequest);
 | 
			
		||||
			}
 | 
			
		||||
			else {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -113,6 +113,12 @@ public interface ServerRequest {
 | 
			
		|||
	 */
 | 
			
		||||
	<T> Optional<T> attribute(String name);
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Return a mutable map of request attributes.
 | 
			
		||||
	 * @return the request attributes
 | 
			
		||||
	 */
 | 
			
		||||
	Map<String, Object> attributes();
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Return the first query parameter with the given name, if present.
 | 
			
		||||
	 * @param name the parameter name
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -114,6 +114,11 @@ public class ServerRequestWrapper implements ServerRequest {
 | 
			
		|||
		return this.delegate.attribute(name);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Map<String, Object> attributes() {
 | 
			
		||||
		return this.delegate.attributes();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Optional<String> queryParam(String name) {
 | 
			
		||||
		return this.delegate.queryParam(name);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -29,6 +29,7 @@ import java.util.Locale;
 | 
			
		|||
import java.util.Map;
 | 
			
		||||
import java.util.Optional;
 | 
			
		||||
import java.util.OptionalLong;
 | 
			
		||||
import java.util.concurrent.ConcurrentHashMap;
 | 
			
		||||
 | 
			
		||||
import reactor.core.publisher.Flux;
 | 
			
		||||
import reactor.core.publisher.Mono;
 | 
			
		||||
| 
						 | 
				
			
			@ -127,6 +128,11 @@ public class MockServerRequest implements ServerRequest {
 | 
			
		|||
		return Optional.ofNullable((S) this.attributes.get(name));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Map<String, Object> attributes() {
 | 
			
		||||
		return this.attributes;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public List<String> queryParams(String name) {
 | 
			
		||||
		return Collections.unmodifiableList(this.queryParams.get(name));
 | 
			
		||||
| 
						 | 
				
			
			@ -182,7 +188,7 @@ public class MockServerRequest implements ServerRequest {
 | 
			
		|||
 | 
			
		||||
		private Object body;
 | 
			
		||||
 | 
			
		||||
		private Map<String, Object> attributes = new LinkedHashMap<>();
 | 
			
		||||
		private Map<String, Object> attributes = new ConcurrentHashMap<>();
 | 
			
		||||
 | 
			
		||||
		private MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue