Skip to content
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

MetricsWebFilter assigns UNKNOWN outcome to 200 OK responses #19367

Closed
hban opened this issue Dec 13, 2019 · 10 comments
Closed

MetricsWebFilter assigns UNKNOWN outcome to 200 OK responses #19367

hban opened this issue Dec 13, 2019 · 10 comments
Assignees
Labels
type: bug A general bug
Milestone

Comments

@hban
Copy link

hban commented Dec 13, 2019

If REST controller doesn't explicitly set status code (so it defaults to 200 OK), MetricsWebFilter will set outcome tag to UNKNOWN instead of SUCCESS.

Example application to reproduce:

package net.example;
 
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
 
@SpringBootApplication
public class ExampleApplication {
 
    public static void main(String[] args) {
        SpringApplication.run(ExampleApplication.class, args);
    }
 
}
 
@RestController
class ExampleController {
 
    @GetMapping("/example")
    Mono<String> example() {
        return Mono.just("EXAMPLE");
    }
 
}
management:
    endpoints:
        web:
            exposure:
                include:
                    - metrics
curl http://localhost:8080/example

curl http://localhost:8080/actuator/metrics/http.server.requests?tag=uri:/example
# availableTags{tag="outcome"} only has "UNKNOWN" value
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Dec 13, 2019
@michaelmcfadyen
Copy link
Contributor

I can confirm this behaviour is present in spring boot 2.2.2

@michaelmcfadyen
Copy link
Contributor

The behaviour looks to be caused by the following method:

There is a discrepancy between how the status code is resolved for the status code tag how it is resolved for the outcome tag.

@bclozel
Copy link
Member

bclozel commented Dec 16, 2019

In spring-projects/spring-framework#21901, we can see that the ServerHttpResponse contract is about modifying the response and falling back on the server defaults (HTTP 200 OK) if nothing has been set by the application.
In gh-18267, we also see that we rely on org.springframework.http.server.reactive.AbstractServerHttpResponse#getStatusCodeValue to handle non-standard status codes. But this method does not itself look into the native server response and can return null.

I'm wondering if org.springframework.http.server.reactive.AbstractServerHttpResponse#getStatusCodeValue should return the underlying default server response status. Of if we should, for consistency, return Outcome.SUCCESS in

public static Tag outcome(ServerWebExchange exchange) {
Integer statusCode = extractStatusCode(exchange);
Outcome outcome = (statusCode != null) ? Outcome.forStatus(statusCode) : Outcome.UNKNOWN;
return outcome.asTag();
}

@rstoyanchev do you think we should rely at all on org.springframework.http.server.reactive.AbstractServerHttpResponse#getStatusCodeValue in the first place - if not, how should we handle non-standard HTTP status codes?

@bclozel bclozel added type: bug A general bug and removed status: waiting-for-triage An issue we've not yet triaged labels Dec 16, 2019
@bclozel bclozel added this to the 2.2.x milestone Dec 16, 2019
@rstoyanchev
Copy link
Contributor

rstoyanchev commented Dec 16, 2019

AbstractServerHttpResponse#getStatusCodeValue could be improved to fall back on getStatusCode() but I'm reluctant to change it because it is the only way to know if the status has been set at all. It is also a bit of an internal method to begin with.

I think you could update WebFluxTags to something like this:

private static Integer extractStatusCode(ServerWebExchange exchange) {
	ServerHttpResponse response = exchange.getResponse();
	Integer statusCode = null;		
	if (response instanceof AbstractServerHttpResponse) {
		statusCode = ((AbstractServerHttpResponse) response).getStatusCodeValue();
	}
	if (statusCode == null) {
		HttpStatus httpStatus = response.getStatusCode();
		statusCode = (httpStatus != null ? httpStatus.value() : null);
	}
	return statusCode;
}

@bclozel
Copy link
Member

bclozel commented Dec 17, 2019

Thanks Rossen, we'll switch from Outcome.UNKNOWN to Outcome.SUCCESS by default, since this will be the default HTTP status if no other information is provided. We can use that opportunity to improve status extraction with the code snippet you suggested. Thanks!

@mbhave
Copy link
Contributor

mbhave commented Jan 7, 2020

@rstoyanchev @bclozel I'm trying to understand why the above snippet is better than what we currently have. Is there any situation where an AbstractServerHttpResponse#getStatusCodeValue would return null but AbstractServerHttpResponse#getStatusCode would not?

@rstoyanchev
Copy link
Contributor

@mbhave maybe what you're missing is the fact that getStatusCode() is overridden in sub-classes which fall back on the underlying server default. In short the situation is whenever the status code is not explicitly set.

@mbhave
Copy link
Contributor

mbhave commented Jan 8, 2020

ah, I see what you mean now. Thanks @rstoyanchev.

@mbhave mbhave self-assigned this Jan 8, 2020
@mbhave mbhave closed this as completed in 7f0573d Jan 10, 2020
@wilkinsona wilkinsona modified the milestones: 2.2.x, 2.2.3 Jan 10, 2020
snicoll pushed a commit to scottfrederick/spring-boot that referenced this issue Jan 10, 2020
This commit also changed the default outcome to SUCCESS

Fixes spring-projectsgh-19367
@rstoyanchev
Copy link
Contributor

Note that with spring-projects/spring-framework#24400 ServerHttpResponse now exposes getRawStatusCode() so you can avoid the downcast to AbstractServerHttpResponse which is now a deprecated method anyway.

izeye added a commit to izeye/spring-boot that referenced this issue Jan 30, 2020
This commit also changes Spring Framework version to 5.2.4.BUILD-SNAPSHOT for the necessary upstream change.

See spring-projects#19367 (comment)
@izeye
Copy link
Contributor

izeye commented Jan 30, 2020

I created #19987 to apply the upstream change.

izeye added a commit to izeye/spring-boot that referenced this issue Jan 30, 2020
This commit also changes Spring Framework version to 5.2.4.BUILD-SNAPSHOT for the necessary upstream change.

See spring-projects#19367 (comment)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug A general bug
Projects
None yet
Development

No branches or pull requests

8 participants