Add metric to include the path as a Gateway metric Tag

Fixes gh-2419
This commit is contained in:
Marta Medio
2021-10-27 12:52:52 +02:00
committed by spencergibb
parent 1c7c05e9bd
commit 24c3add1f1
5 changed files with 146 additions and 0 deletions

View File

@@ -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.

View File

@@ -1898,6 +1898,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.

View File

@@ -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();

View File

@@ -0,0 +1,57 @@
/*
* 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_ROUTE_ATTR;
/**
* @author Marta Medio
* @author Alberto C. Ríos
*/
public class GatewayPathTagsProvider implements GatewayTagsProvider {
private static final String START_PATH_PATTERN = "Paths: [";
private static final String END_PATH_PATTERN = "], match";
@Override
public Tags apply(ServerWebExchange exchange) {
Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
if (route != null) {
String rawPredicate = route.getPredicate().toString();
if (predicateContainsPath(rawPredicate)) {
int beginIndex = rawPredicate.indexOf(START_PATH_PATTERN) + START_PATH_PATTERN.length();
String predicate = rawPredicate.substring(beginIndex, rawPredicate.indexOf(END_PATH_PATTERN));
return Tags.of("path", predicate);
}
}
return Tags.empty();
}
private boolean predicateContainsPath(String rawPredicate) {
return rawPredicate != null && rawPredicate.contains(START_PATH_PATTERN)
&& rawPredicate.contains(END_PATH_PATTERN);
}
}

View File

@@ -0,0 +1,77 @@
/*
* 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.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import io.micrometer.core.instrument.Tags;
import org.junit.jupiter.api.Test;
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_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 = Stream.of("/git/**").collect(Collectors.toList());
Route route = Route.async().id("git").uri(ROUTE_URI).predicate(
new PathRoutePredicateFactory().apply(new PathRoutePredicateFactory.Config().setPatterns(pathList)))
.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(1);
assertThat(tags.stream().anyMatch(tag -> "path".equals(tag.getKey()))).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);
}
}