-
Notifications
You must be signed in to change notification settings - Fork 38.7k
Description
Spring Boot: 3.1.5
Spring Framework: 6.0.13
In Spring 3 micrometer integration has been reworked to use Micrometer 1.10 Observation. It works in the default setup but in case there is a filter
(ExchangeFilterFunction
) that changes the underlining request, metric is reported with incorrect tags.
It happens because current implementation is initializing ClientRequestObservationContext
using the original ClientRequest
Line 455 in e12269e
observationContext.setRequest(request); |
and any changes to the request after this point are not visible to the observation context
Line 92 in e12269e
return "http " + context.getRequest().method().name().toLowerCase(); |
Similar scenario was supported in the previous version by adding custom filters before MetricsWebClientFilterFunction
that reported the same metric before.
Here is a test to reproduce the above issue. In this test original request uses POST
http method but filter is changing is to GET
. The metric is still reported with the original POST
method.
iterable contents differ at index [3], expected: <tag(method=GET)> but was: <tag(method=POST)>
Expected :tag(method=GET)
Actual :tag(method=POST)
@SpringBootTest(
properties = {"management.metrics.tags.service-name=test"}
)
class WebClientObservationTest {
@Autowired
private WebClient.Builder webClientBuilder;
@Autowired
private MeterRegistry meterRegistry;
@Test
void httpClientRequestsMetrics() {
var webClient = webClientBuilder
.filter(new ClientRequestExchangeFilter())
.build();
var req = webClient.post()
.uri("http://api.zippopotam.us/us/{zip}", 98121)
.retrieve()
.bodyToMono(String.class);
StepVerifier.create(req)
.assertNext(res -> {
assertNotNull(res);
})
.verifyComplete();
Meter meter = meterRegistry.find("http.client.requests")
.meter();
assertNotNull(meter);
List<Tag> expectedTags = Arrays.asList(
Tag.of("client.name", "api.zippopotam.us"),
Tag.of("error", "none"),
Tag.of("exception", "none"),
Tag.of("method", "GET"),
Tag.of("outcome", "SUCCESS"),
Tag.of("service-name", "test"),
Tag.of("status", "200"),
Tag.of("uri", "/us/{zip}")
);
assertIterableEquals(expectedTags, meter.getId().getTags());
}
static class ClientRequestExchangeFilter implements ExchangeFilterFunction {
@Override
public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {
ClientRequest newRequest = ClientRequest.from(request)
.method(HttpMethod.GET)
.build();
return next.exchange(newRequest);
}
}
@TestConfiguration
public static class MockConfiguration {
@Bean
public MeterRegistry simpleMeterRegistry() {
return new SimpleMeterRegistry();
}
}
}