From 7da6e9359736c03e5d3be7ea5a620be00ff8b76c Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Fri, 27 Jan 2023 14:42:08 +0100 Subject: [PATCH] Set WebClient Observation as current in reactor context Prior to this commit, the `DefaultWebClient` would be instrumented for client observations and would start/stop a `"http.client.requests"` observation. This would not set this new observation as the current one in the Reactor context under `ObservationThreadLocalAccessor.KEY`. This means that potential child observations would not detect it as their parent; this can happen if the Reactor Netty `HttpClient` observation is enabled. This commit ensures that the reactor context is properly populated for upstream operators. Fixes gh-29891 --- .../function/client/DefaultWebClient.java | 5 +++-- .../client/WebClientObservationTests.java | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClient.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClient.java index 2f099e9e40e..5d4f6f27ab2 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClient.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 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. @@ -478,7 +478,8 @@ class DefaultWebClient implements WebClient { observationContext.setAborted(true); observation.stop(); }) - .doOnTerminate(observation::stop); + .doOnTerminate(observation::stop) + .contextWrite(context -> context.put(ObservationThreadLocalAccessor.KEY, observation)); }); } diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/WebClientObservationTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/WebClientObservationTests.java index d2b6e47520d..3131cbda3e3 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/WebClientObservationTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/WebClientObservationTests.java @@ -114,6 +114,25 @@ class WebClientObservationTests { .hasLowCardinalityKeyValue("status", "CLIENT_ERROR"); } + @Test + void setsCurrentObservationInReactorContext() { + ExchangeFilterFunction assertionFilter = new ExchangeFilterFunction() { + @Override + public Mono filter(ClientRequest request, ExchangeFunction chain) { + return chain.exchange(request).contextWrite(context -> { + Observation currentObservation = context.get(ObservationThreadLocalAccessor.KEY); + assertThat(currentObservation).isNotNull(); + assertThat(currentObservation.getContext()).isInstanceOf(ClientRequestObservationContext.class); + return context; + }); + } + }; + this.builder.filter(assertionFilter).build().get().uri("/resource/{id}", 42) + .retrieve().bodyToMono(Void.class) + .block(Duration.ofSeconds(10)); + verifyAndGetRequest(); + } + private TestObservationRegistryAssert.TestObservationRegistryAssertReturningObservationContextAssert assertThatHttpObservation() { return TestObservationRegistryAssert.assertThat(this.observationRegistry) .hasObservationWithNameEqualTo("http.client.requests").that();