Skip to content

Commit b910a54

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

File tree

2 files changed

+71
-1
lines changed

2 files changed

+71
-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: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,65 @@ 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
334+
.getAttribute(ObservationFilterChainDecorator.ATTRIBUTE);
335+
assertThat(parentObservation).isNotNull();
336+
337+
// simulate dispatching error-related request
338+
Filter errorRelatedFilter = ObservationFilterChainDecorator.FilterObservation
339+
.create(Observation.createNotStarted("wrap", registry))
340+
.wrap(fcp);
341+
ServletRequest errorRelatedRequest = new MockHttpServletRequest("GET", "/error");
342+
errorRelatedRequest.setAttribute(ObservationFilterChainDecorator.ATTRIBUTE, parentObservation);
343+
errorRelatedFilter.doFilter(errorRelatedRequest, new MockHttpServletResponse(), this.chain);
344+
345+
ArgumentCaptor<Observation.Context> captor = ArgumentCaptor.forClass(Observation.Context.class);
346+
verify(handler, times(8)).onStart(captor.capture());
347+
verify(handler, times(8)).onStop(any());
348+
List<Observation.Context> contexts = captor.getAllValues();
349+
350+
Observation.Context initialRequestObservationContextBefore = contexts.get(1);
351+
Observation.Context initialRequestObservationContextAfter = contexts.get(3);
352+
assertFilterChainObservation(initialRequestObservationContextBefore, "before", 1);
353+
assertFilterChainObservation(initialRequestObservationContextAfter, "after", 1);
354+
355+
assertThat(initialRequestObservationContextBefore.getParentObservation()).isNotNull();
356+
assertThat(initialRequestObservationContextBefore.getParentObservation())
357+
.isSameAs(initialRequestObservationContextAfter.getParentObservation());
358+
359+
Observation.Context errorRelatedRequestObservationContextBefore = contexts.get(5);
360+
Observation.Context errorRelatedRequestObservationContextAfter = contexts.get(7);
361+
assertFilterChainObservation(errorRelatedRequestObservationContextBefore, "before", 1);
362+
assertFilterChainObservation(errorRelatedRequestObservationContextAfter, "after", 1);
363+
364+
assertThat(errorRelatedRequestObservationContextBefore.getParentObservation()).isNotNull();
365+
assertThat(errorRelatedRequestObservationContextBefore.getParentObservation())
366+
.isSameAs(initialRequestObservationContextBefore.getParentObservation());
367+
assertThat(errorRelatedRequestObservationContextAfter.getParentObservation()).isNotNull();
368+
assertThat(errorRelatedRequestObservationContextAfter.getParentObservation())
369+
.isSameAs(initialRequestObservationContextBefore.getParentObservation());
370+
}
371+
313372
@Test
314373
public void doFilterWhenMultipleFiltersThenObservationRegistryObserves() throws Exception {
315374
ObservationHandler<Observation.Context> handler = mock(ObservationHandler.class);

0 commit comments

Comments
 (0)