-
Notifications
You must be signed in to change notification settings - Fork 41.4k
Align WebFluxTags#uri() with WebMvcTags#uri() #15609
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
So you map all the other service's url to |
This doesn't know about the gateway |
@youzipi This should get rid of the tag explosion you see, at least. And it makes sense to align it with the variant of WebMvcTags, if you ask me. Having that said, I understand your conceirns that more URLs will be potentially @bclozel What do you think? (Given you self-assigned this) Cheers |
Thanks a bunch @dreis2211 ! @youzipi Spencer and Christoph are right, there's no way to have both the full uri information without being aware of the pattern and avoid the tag explosion at the same time. I guess a gateway should be more concerned about producing local metrics, and each application should publish its own to a shared repository. |
Where is this code available? Or which version of springBoot is it available? |
As shown by the milestone to which this issue is assigned, the changes we made in Spring Boot 2.0.8. You can also see in the commit that closed this PR all of the tags that include the change. |
I just posted this https://stackoverflow.com/questions/58724623/webfluxtagsuri-returning-wrong-tags before finding this. Maybe you can give some clarification there what exchange.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE); does and what one has to do for the request to always get a match here since now lots of my request get null back here even when they have a path. |
I am using Spring boot actuator 2.3.0 but still seeing the same issue. Please fix it asap as it is blocking my metrics in grafana dashboard. http_server_requests_seconds_bucket{exception="None",method="GET",outcome="CLIENT_ERROR",status="401",uri="UNKNOWN",le="22.906492245",} 2.0 or else atleast give me some work around to fix this. @ALL - Did anyone overwritten this jar to get the correct URL instead of Unknown ??? |
@veerumallavarapu Your question is very similar to #16208 - without this change, you might get a case of tag cardinality explosion: this would make things even worse in your grafana dashboard (or worse, OOM errors). To work around this problem, please ensure that the handler is mapped by a pattern. For more guidance about your particular case, please create a question on StackOverflow with more information, such as the HTTP request and the code handling the request. |
I did not get "To work around this problem, please ensure that the handler is mapped by a pattern." where should I do this ? |
@veerumallavarapu the raw metric data doesn't tell much about this. There might be no way to work around this. It really depends on what's handling the request. A question on StackOverflow with the requested details would be more productive. |
Is this not possible to set the Pattern again ? |
Could someone please clarify why this change was needed? Several people claim that it gets rid of tag explosion, but didn't #14173 take care of that already? In particular the following bean should ensure that a configurable maximum number of different Line 80 in 9460d74
Is this a case of users having not upgraded to the Spring release containing #14173 when they noticed this issue, leading to two different fixes for the same problem only a few months apart? Having layered this on top of the existing implementation has several disadvantages and leads to various problems:
If other people agree this should be reverted, I'm happy to submit a pull request. I'm also happy to be enlightened if #14173 and this PR happen to be somehow mutually exclusive. :) |
Thanks for the feedback, @PyvesB. The change was made to align the behaviour of WebFlux's metrics with those of MVC. The meter filter and emitting Emitting |
We wanted to add the URI to all requests, so we added this Bean: /**
* The URI {@link io.micrometer.core.instrument.Tag} is occasionally set as <b>UNKNOWN</b> when the {@link HandlerMapping#BEST_MATCHING_PATTERN_ATTRIBUTE}
* is not set in the exchange attributes. To counter that we added this custom WebFluxTagsContributor.
*/
@Bean
WebFluxTagsContributor uriTagContributorForConnectionProblems()
{
return (exchange, ex) -> willExchangeUriBeingParsed(exchange)
? Tags.empty()
: Tags.of("uri", exchange.getRequest().getPath().value());
}
private static boolean willExchangeUriBeingParsed(final ServerWebExchange exchange)
{
return exchange.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE) != null;
} |
Thank you @Illutax this works, but WebFluxTagsContributor is deprecated since 3.0.0 |
@sw-fox As mentioned in the javadoc the replacement is |
I don't know if I was having the same problem, but none of my Path predicates were being matched as uri in http_server_requests_*, so I created a ServerRequestObservationConvention bean that matches all Path predicates in the configuration, maybe this can help someone with this problem as a workaround. /**
* This class is a workaround because spring cloud gateway is not setting the pathPattern to the context, which makes
* metrics of known patterns have UNKNOWN uri.
* <p>
* This implementation uses the Path pattern predicates defined in {@link GatewayProperties}, so it probably won't work for
* programmatically defined path predicates.
*/
@NonNullApi
@Component
public class PathMatchingObservationConvention extends DefaultServerRequestObservationConvention {
private static final Logger LOGGER = LoggerFactory.getLogger(
com.tripadvisor.dapr.gateway.PathMatchingObservationConvention.class);
private final List<Tuple2<String, PathPattern>> pathPatterns;
@Autowired
public PathMatchingObservationConvention(final GatewayProperties gatewayProperties) {
pathPatterns = new ArrayList<>();
gatewayProperties.getRoutes().forEach(routeDefinition -> {
pathPatterns.addAll(routeDefinition.getPredicates().stream().filter(
predicateDefinition -> "Path".equals(predicateDefinition.getName())).flatMap(
predicateDefinition -> predicateDefinition.getArgs().values().stream()).map(pattern -> {
PathPatternParser parser = PathPatternParser.defaultInstance;
pattern = parser.initFullPathPattern(pattern);
return Tuple.of(pattern, parser.parse(pattern));
}).toList());
});
}
/**
* Method overriden to set the pathPattern to the context based on the defined Path predicates in the configuration.
* This makes 'uri' return matches patterns when getting the low cardinality tags.
*
* @param context the context
* @return the uri
* @see <a href="https://github.com/spring-projects/spring-boot/pull/15609#issuecomment-709987823">github issue</a>
*/
@Override
public KeyValues getLowCardinalityKeyValues(final ServerRequestObservationContext context) {
if (context.getPathPattern() == null) {
pathPatterns.stream().filter(
tuple -> tuple.map2(pattern -> pattern.matches(context.getCarrier().getPath()))._2()).map(
Tuple2::_1).findFirst().ifPresent(context::setPathPattern);
}
return super.getLowCardinalityKeyValues(context);
}
} |
@massahud Thanks it works, however it only shows the predicate but not the whole uri. For instance if gateway enroutes to a microservice that exposes 5 endpoints, and gateway lets access to that microservice through its predicate, the metrics only shows for that predicate not distinguishing the calls for which of the 5 endpoints. |
@esteban-coder maybe you are looking at the server metrics (http_server_requests_*) , upstream endpoints usually appear in the |
Issue is still there in Spring Boot 3.3.0. Here is the solution I implemented:
|
@danparisi I believe this is a Spring Cloud Gateway issue, probably a different one than spring-cloud/spring-cloud-gateway/issues#891 though. I believe that this might be done on purpose, if the gateway doesn't know of specific uri patterns. Please create a new Spring Cloud Gateway issue to discuss this. Note that your workaround is vulnerable to denial of service attacks on your app because it creates cardinality explosion for metrics. |
@danparisi , Thanks for the solution. This is exactly what I have been looking for.. |
@srinivasboini Please ensure you're aware of Brian's concern about denial of service:
|
Hi @wilkinsona , My requirement is bit different. I need to pass the gateway route path predicate in the uri attribute. I don't need to pass the exact url that is being called. gateway route
if I'm calling with /test/app/100 i need to get the uri as /test/app/{id} and not the exact high cardinality value. I have changed the solution a bit according to my need.
@bclozel, could you have a look at this please ? and thanks @wilkinsona for highlighting this. |
@srinivasboini if you can get a route pattern as an attribute and use this to avoid cardinality explosion it's fine. |
Above solution was showing problem in the context for the reactive springboot gateway this I have personally used to fix it Code: @Configuration
@NonNullApi
public class MicrometerConfiguration {
private static final String KEY_URI = "uri";
private static final String UNKNOWN = "UNKNOWN";
@Bean
public DefaultServerRequestObservationConvention uriTagContributorForObservationApi() {
return new DefaultServerRequestObservationConvention() {
@Override
public KeyValues getLowCardinalityKeyValues(ServerRequestObservationContext context) {
KeyValues lowCardinalityKeyValues = super.getLowCardinalityKeyValues(context);
ServerHttpRequest request = context.getCarrier();
if (isUriTagNullOrUnknown(context, lowCardinalityKeyValues)) {
return lowCardinalityKeyValues
.and(KeyValue.of(KEY_URI, request.getPath().value()));
}
return lowCardinalityKeyValues;
}
private static boolean isUriTagNullOrUnknown(ServerRequestObservationContext context, KeyValues lowCardinalityKeyValues) {
Optional<KeyValue> uriKeyValue = lowCardinalityKeyValues.stream()
.filter(keyValue -> ServerHttpObservationDocumentation.LowCardinalityKeyNames.URI.name()
.equals(keyValue.getKey()))
.findFirst();
return (uriKeyValue.isEmpty() || UNKNOWN.equals(uriKeyValue.get().getValue()));
}
};
}
} IMPORTS
|
@Priyamomer I don't recommend blindly adding the request path, it can generate too many metrics if you have parameterized path patterns, also an attack with random paths will affect your metrics collector. This example shows how to use PathPatternMatcher to create a PathPattern and match with the request path. |
Yes that we should add you are right |
Hi,
this PR is an attempt to fix #15608 by aligning
WebFluxTags#uri()
withWebMvcTags#uri()
.Let me know what you think.
Cheers,
Christoph