Skip to content

Commit 4790f73

Browse files
committed
Fix traceId discrepancy in case error in servlet web
Signed-off-by: Nikita Konev <[email protected]>
1 parent 16c9794 commit 4790f73

File tree

2 files changed

+67
-1
lines changed

2 files changed

+67
-1
lines changed

web/src/main/java/org/springframework/security/web/ObservationFilterChainDecorator.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,14 @@
4646
* wraps the chain in before and after observations
4747
*
4848
* @author Josh Cummings
49+
* @author Nikita Konev
4950
* @since 6.0
5051
*/
5152
public final class ObservationFilterChainDecorator implements FilterChainProxy.FilterChainDecorator {
5253

5354
private static final Log logger = LogFactory.getLog(FilterChainProxy.class);
5455

55-
private static final String ATTRIBUTE = ObservationFilterChainDecorator.class + ".observation";
56+
static final String ATTRIBUTE = ObservationFilterChainDecorator.class + ".observation";
5657

5758
static final String UNSECURED_OBSERVATION_NAME = "spring.security.http.unsecured.requests";
5859

@@ -250,6 +251,16 @@ private void wrapFilter(ServletRequest request, ServletResponse response, Filter
250251
private AroundFilterObservation parent(HttpServletRequest request) {
251252
FilterChainObservationContext beforeContext = FilterChainObservationContext.before();
252253
FilterChainObservationContext afterContext = FilterChainObservationContext.after();
254+
255+
AroundFilterObservation existingParentObservation = (AroundFilterObservation) request
256+
.getAttribute(ATTRIBUTE);
257+
if (existingParentObservation != null) {
258+
beforeContext
259+
.setParentObservation(existingParentObservation.before().getContext().getParentObservation());
260+
afterContext
261+
.setParentObservation(existingParentObservation.after().getContext().getParentObservation());
262+
}
263+
253264
Observation before = Observation.createNotStarted(this.convention, () -> beforeContext, this.registry);
254265
Observation after = Observation.createNotStarted(this.convention, () -> afterContext, this.registry);
255266
AroundFilterObservation parent = AroundFilterObservation.create(before, after);

web/src/test/java/org/springframework/security/web/FilterChainProxyTests.java

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,61 @@ public void doFilterWhenMatchesThenObservationRegistryObserves() throws Exceptio
310310
assertFilterChainObservation(contexts.next(), "after", 1);
311311
}
312312

313+
// gh-12610
314+
@Test
315+
void parentObservationIsTakenIntoAccountDuringDispatchError() throws Exception {
316+
ObservationHandler<Observation.Context> handler = mock(ObservationHandler.class);
317+
given(handler.supportsContext(any())).willReturn(true);
318+
ObservationRegistry registry = ObservationRegistry.create();
319+
registry.observationConfig().observationHandler(handler);
320+
321+
given(this.matcher.matches(any())).willReturn(true);
322+
SecurityFilterChain sec = new DefaultSecurityFilterChain(this.matcher, Arrays.asList(this.filter));
323+
FilterChainProxy fcp = new FilterChainProxy(sec);
324+
fcp.setFilterChainDecorator(new ObservationFilterChainDecorator(registry));
325+
Filter initialFilter = ObservationFilterChainDecorator.FilterObservation
326+
.create(Observation.createNotStarted("wrap", registry))
327+
.wrap(fcp);
328+
329+
ServletRequest initialRequest = new MockHttpServletRequest("GET", "/");
330+
initialFilter.doFilter(initialRequest, new MockHttpServletResponse(), this.chain);
331+
332+
// simulate request attribute copying in case dispatching to ERROR
333+
ObservationFilterChainDecorator.AroundFilterObservation parentObservation = (ObservationFilterChainDecorator.AroundFilterObservation) initialRequest.getAttribute(ObservationFilterChainDecorator.ATTRIBUTE);
334+
assertThat(parentObservation).isNotNull();
335+
336+
// simulate dispatching error-related request
337+
Filter errorRelatedFilter = ObservationFilterChainDecorator.FilterObservation
338+
.create(Observation.createNotStarted("wrap", registry))
339+
.wrap(fcp);
340+
ServletRequest errorRelatedRequest = new MockHttpServletRequest("GET", "/error");
341+
errorRelatedRequest.setAttribute(ObservationFilterChainDecorator.ATTRIBUTE, parentObservation);
342+
errorRelatedFilter.doFilter(errorRelatedRequest, new MockHttpServletResponse(), this.chain);
343+
344+
ArgumentCaptor<Observation.Context> captor = ArgumentCaptor.forClass(Observation.Context.class);
345+
verify(handler, times(8)).onStart(captor.capture());
346+
verify(handler, times(8)).onStop(any());
347+
List<Observation.Context> contexts = captor.getAllValues();
348+
349+
Observation.Context initialRequestObservationContextBefore = contexts.get(1);
350+
Observation.Context initialRequestObservationContextAfter = contexts.get(3);
351+
assertFilterChainObservation(initialRequestObservationContextBefore, "before", 1);
352+
assertFilterChainObservation(initialRequestObservationContextAfter, "after", 1);
353+
354+
assertThat(initialRequestObservationContextBefore.getParentObservation()).isNotNull();
355+
assertThat(initialRequestObservationContextBefore.getParentObservation()).isSameAs(initialRequestObservationContextAfter.getParentObservation());
356+
357+
Observation.Context errorRelatedRequestObservationContextBefore = contexts.get(5);
358+
Observation.Context errorRelatedRequestObservationContextAfter = contexts.get(7);
359+
assertFilterChainObservation(errorRelatedRequestObservationContextBefore, "before", 1);
360+
assertFilterChainObservation(errorRelatedRequestObservationContextAfter, "after", 1);
361+
362+
assertThat(errorRelatedRequestObservationContextBefore.getParentObservation()).isNotNull();
363+
assertThat(errorRelatedRequestObservationContextBefore.getParentObservation()).isSameAs(initialRequestObservationContextBefore.getParentObservation());
364+
assertThat(errorRelatedRequestObservationContextAfter.getParentObservation()).isNotNull();
365+
assertThat(errorRelatedRequestObservationContextAfter.getParentObservation()).isSameAs(initialRequestObservationContextBefore.getParentObservation());
366+
}
367+
313368
@Test
314369
public void doFilterWhenMultipleFiltersThenObservationRegistryObserves() throws Exception {
315370
ObservationHandler<Observation.Context> handler = mock(ObservationHandler.class);

0 commit comments

Comments
 (0)