Merge branch '3.0.x'
This commit is contained in:
@@ -107,6 +107,7 @@
|
||||
|spring.cloud.gateway.metrics.enabled | `false` | Enables the collection of metrics data.
|
||||
|spring.cloud.gateway.metrics.prefix | `spring.cloud.gateway` | The prefix of all metrics emitted by gateway.
|
||||
|spring.cloud.gateway.metrics.tags | | Tags map that added to metrics.
|
||||
|spring.cloud.gateway.metrics.tags.path.enabled | `false` | If the collection of metrics data is enabled, enables an extra metric data tag by path.
|
||||
|spring.cloud.gateway.predicate.after.enabled | `true` | Enables the after predicate.
|
||||
|spring.cloud.gateway.predicate.before.enabled | `true` | Enables the before predicate.
|
||||
|spring.cloud.gateway.predicate.between.enabled | `true` | Enables the between predicate.
|
||||
|
||||
@@ -1944,6 +1944,10 @@ To enable gateway metrics, add spring-boot-starter-actuator as a project depende
|
||||
* `httpStatusCode`: The HTTP Status of the request returned to the client.
|
||||
* `httpMethod`: The HTTP method used for the request.
|
||||
|
||||
In addition, through the property `spring.cloud.gateway.metrics.tags.path.enabled` (by default, set to false), you can activate an extra metric with the tag:
|
||||
|
||||
* `path`: Path of the request.
|
||||
|
||||
These metrics are then available to be scraped from `/actuator/metrics/spring.cloud.gateway.requests` and can be easily integrated with Prometheus to create a link:images/gateway-grafana-dashboard.jpeg[Grafana] link:gateway-grafana-dashboard.json[dashboard].
|
||||
|
||||
NOTE: To enable the prometheus endpoint, add `micrometer-registry-prometheus` as a project dependency.
|
||||
|
||||
@@ -33,6 +33,7 @@ import org.springframework.cloud.gateway.filter.GatewayMetricsFilter;
|
||||
import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
|
||||
import org.springframework.cloud.gateway.route.RouteDefinitionMetrics;
|
||||
import org.springframework.cloud.gateway.support.tagsprovider.GatewayHttpTagsProvider;
|
||||
import org.springframework.cloud.gateway.support.tagsprovider.GatewayPathTagsProvider;
|
||||
import org.springframework.cloud.gateway.support.tagsprovider.GatewayRouteTagsProvider;
|
||||
import org.springframework.cloud.gateway.support.tagsprovider.GatewayTagsProvider;
|
||||
import org.springframework.cloud.gateway.support.tagsprovider.PropertiesTagsProvider;
|
||||
@@ -53,6 +54,12 @@ public class GatewayMetricsAutoConfiguration {
|
||||
return new GatewayHttpTagsProvider();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(name = GatewayProperties.PREFIX + ".metrics.tags.path.enabled")
|
||||
public GatewayPathTagsProvider gatewayPathTagsProvider() {
|
||||
return new GatewayPathTagsProvider();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GatewayRouteTagsProvider gatewayRouteTagsProvider() {
|
||||
return new GatewayRouteTagsProvider();
|
||||
|
||||
@@ -32,6 +32,9 @@ import org.springframework.web.util.pattern.PathPattern;
|
||||
import org.springframework.web.util.pattern.PathPattern.PathMatchInfo;
|
||||
import org.springframework.web.util.pattern.PathPatternParser;
|
||||
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_PREDICATE_MATCHED_PATH_ATTR;
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_PREDICATE_MATCHED_PATH_ROUTE_ID_ATTR;
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_PREDICATE_ROUTE_ATTR;
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.putUriTemplateVariables;
|
||||
import static org.springframework.http.server.PathContainer.parsePath;
|
||||
|
||||
@@ -101,6 +104,12 @@ public class PathRoutePredicateFactory extends AbstractRoutePredicateFactory<Pat
|
||||
traceMatch("Pattern", match.getPatternString(), path, true);
|
||||
PathMatchInfo pathMatchInfo = match.matchAndExtract(path);
|
||||
putUriTemplateVariables(exchange, pathMatchInfo.getUriVariables());
|
||||
exchange.getAttributes().put(GATEWAY_PREDICATE_MATCHED_PATH_ATTR, match.getPatternString());
|
||||
String routeId = (String) exchange.getAttributes().get(GATEWAY_PREDICATE_ROUTE_ATTR);
|
||||
if (routeId != null) {
|
||||
// populated in RoutePredicateHandlerMapping
|
||||
exchange.getAttributes().put(GATEWAY_PREDICATE_MATCHED_PATH_ROUTE_ID_ATTR, routeId);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -110,6 +110,17 @@ public final class ServerWebExchangeUtils {
|
||||
*/
|
||||
public static final String GATEWAY_PREDICATE_ROUTE_ATTR = qualify("gatewayPredicateRouteAttr");
|
||||
|
||||
/**
|
||||
* Gateway predicate matched path attribute name.
|
||||
*/
|
||||
public static final String GATEWAY_PREDICATE_MATCHED_PATH_ATTR = qualify("gatewayPredicateMatchedPathAttr");
|
||||
|
||||
/**
|
||||
* Gateway predicate matched path route id attribute name.
|
||||
*/
|
||||
public static final String GATEWAY_PREDICATE_MATCHED_PATH_ROUTE_ID_ATTR = qualify(
|
||||
"gatewayPredicateMatchedPathRouteIdAttr");
|
||||
|
||||
/**
|
||||
* Weight attribute name.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2013-2020 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.support.tagsprovider;
|
||||
|
||||
import io.micrometer.core.instrument.Tags;
|
||||
|
||||
import org.springframework.cloud.gateway.route.Route;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_PREDICATE_MATCHED_PATH_ATTR;
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_PREDICATE_MATCHED_PATH_ROUTE_ID_ATTR;
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR;
|
||||
|
||||
/**
|
||||
* @author Marta Medio
|
||||
* @author Alberto C. Ríos
|
||||
*/
|
||||
public class GatewayPathTagsProvider implements GatewayTagsProvider {
|
||||
|
||||
@Override
|
||||
public Tags apply(ServerWebExchange exchange) {
|
||||
Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
|
||||
|
||||
if (route != null) {
|
||||
String matchedPathRouteId = exchange.getAttribute(GATEWAY_PREDICATE_MATCHED_PATH_ROUTE_ID_ATTR);
|
||||
String matchedPath = exchange.getAttribute(GATEWAY_PREDICATE_MATCHED_PATH_ATTR);
|
||||
|
||||
// check that the matched path belongs to the route that was actually
|
||||
// selected.
|
||||
if (route.getId().equals(matchedPathRouteId) && matchedPath != null) {
|
||||
return Tags.of("path", matchedPath);
|
||||
}
|
||||
}
|
||||
|
||||
return Tags.empty();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright 2013-2020 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.support.tagsprovider;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import io.micrometer.core.instrument.Tags;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.cloud.gateway.handler.predicate.HostRoutePredicateFactory;
|
||||
import org.springframework.cloud.gateway.handler.predicate.MethodRoutePredicateFactory;
|
||||
import org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory;
|
||||
import org.springframework.cloud.gateway.route.Route;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.mock.web.server.MockServerWebExchange;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_PREDICATE_MATCHED_PATH_ATTR;
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_PREDICATE_MATCHED_PATH_ROUTE_ID_ATTR;
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR;
|
||||
|
||||
/**
|
||||
* @author Marta Medio
|
||||
* @author Alberto C. Ríos
|
||||
*/
|
||||
public class GatewayPathTagsProviderTests {
|
||||
|
||||
private final GatewayPathTagsProvider pathTagsProvider = new GatewayPathTagsProvider();
|
||||
|
||||
private static final String ROUTE_URI = "http://gatewaytagsprovider.org:80";
|
||||
|
||||
@Test
|
||||
void addPathToRoutes() {
|
||||
List<String> pathList = Collections.singletonList("/git/**");
|
||||
|
||||
PathRoutePredicateFactory.Config pathConfig = new PathRoutePredicateFactory.Config().setPatterns(pathList);
|
||||
HostRoutePredicateFactory.Config hostConfig = new HostRoutePredicateFactory.Config()
|
||||
.setPatterns(Collections.singletonList("**.myhost.com"));
|
||||
Route route = Route.async().id("git").uri(ROUTE_URI).predicate(new PathRoutePredicateFactory().apply(pathConfig)
|
||||
.and(new HostRoutePredicateFactory().apply(hostConfig))).build();
|
||||
|
||||
ServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(ROUTE_URI).build());
|
||||
exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, route);
|
||||
exchange.getAttributes().put(GATEWAY_PREDICATE_MATCHED_PATH_ATTR, pathList.get(0));
|
||||
exchange.getAttributes().put(GATEWAY_PREDICATE_MATCHED_PATH_ROUTE_ID_ATTR, route.getId());
|
||||
|
||||
Tags tags = pathTagsProvider.apply(exchange);
|
||||
assertThat(tags.stream().count()).isEqualTo(1);
|
||||
assertThat(tags.stream().anyMatch(tag -> "path".equals(tag.getKey()) && tag.getValue().equals(pathList.get(0))))
|
||||
.isEqualTo(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
void addsMultiplePathToRoutes() {
|
||||
List<String> pathList = Collections.singletonList("/git/**");
|
||||
List<String> pathList2 = Collections.singletonList("/git2/**");
|
||||
|
||||
PathRoutePredicateFactory.Config pathConfig = new PathRoutePredicateFactory.Config().setPatterns(pathList);
|
||||
PathRoutePredicateFactory.Config pathConfig2 = new PathRoutePredicateFactory.Config().setPatterns(pathList2);
|
||||
Route route = Route.async().id("git").uri(ROUTE_URI).predicate(new PathRoutePredicateFactory().apply(pathConfig)
|
||||
.or(new PathRoutePredicateFactory().apply(pathConfig2))).build();
|
||||
|
||||
ServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(ROUTE_URI).build());
|
||||
exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, route);
|
||||
exchange.getAttributes().put(GATEWAY_PREDICATE_MATCHED_PATH_ATTR, pathList2.get(0));
|
||||
exchange.getAttributes().put(GATEWAY_PREDICATE_MATCHED_PATH_ROUTE_ID_ATTR, route.getId());
|
||||
|
||||
Tags tags = pathTagsProvider.apply(exchange);
|
||||
assertThat(tags.stream().count()).isEqualTo(1);
|
||||
assertThat(
|
||||
tags.stream().anyMatch(tag -> "path".equals(tag.getKey()) && tag.getValue().equals(pathList2.get(0))))
|
||||
.isEqualTo(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
void ignoreRoutesWithoutPath() {
|
||||
MethodRoutePredicateFactory.Config config = new MethodRoutePredicateFactory.Config();
|
||||
config.setMethods(HttpMethod.GET);
|
||||
Route route = Route.async().id("empty").uri(ROUTE_URI)
|
||||
.predicate(new MethodRoutePredicateFactory().apply(config)).build();
|
||||
|
||||
ServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(ROUTE_URI).build());
|
||||
exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, route);
|
||||
|
||||
Tags tags = pathTagsProvider.apply(exchange);
|
||||
assertThat(tags.stream().count()).isEqualTo(0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import org.junit.runner.RunWith
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
|
||||
import org.springframework.boot.test.context.SpringBootTest
|
||||
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.mock.http.server.reactive.MockServerHttpRequest
|
||||
import org.springframework.mock.web.server.MockServerWebExchange
|
||||
@@ -72,7 +73,10 @@ class RouteDslTests {
|
||||
val sampleExchange: ServerWebExchange = MockServerWebExchange.from(MockServerHttpRequest.get("/image/webp")
|
||||
.header("Host", "test.abc.org").build())
|
||||
|
||||
val filteredRoutes = routeLocator.routes.filter({ it.predicate.apply(sampleExchange).toMono().block() })
|
||||
val filteredRoutes = routeLocator.routes.filter({
|
||||
sampleExchange.attributes.put(ServerWebExchangeUtils.GATEWAY_PREDICATE_ROUTE_ATTR, it.id)
|
||||
it.predicate.apply(sampleExchange).toMono().block()
|
||||
})
|
||||
|
||||
StepVerifier.create(filteredRoutes)
|
||||
.expectNextMatches({
|
||||
|
||||
Reference in New Issue
Block a user