Affects: 6.0.9

Using SpringBoot 3.1.0, miss log correlation when handling customized exceptions thrown by customized ErrorController. What happens if openScope after observation stopped?

Question submitted on Stack Overflow.


// Throw Exception in filter

public class MyFilter extends GenericFilterBean {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
      // ...
      throw new IllegalArgumentException("IllegalArgumentException");
    }
}

// Customized ErrorController

public class MyErrorController extends AbstractErrorController {
    @RequestMapping
    public ResponseEntity<Map<String, Object>> handler(HttpServletRequest request) {
        // ...
        Object ex = request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
        throw new MyErrorControllerException("requestInfo", (Throwable) ex, status); 
    }
}

// Customized ExceptionHandler

@ExceptionHandler(Throwable.class)
public ResponseEntity<MessageSourceResult> handler(Throwable ex, HttpServletRequest request, HttpServletResponse response) {
    Observation observation = (Observation) request.getAttribute(ServerHttpObservationFilter.class.getName() + ".observation");
    if (null != observation) {

**        // If I want to get the traceId here, what should I do? Is the following code correct? **
        try (Observation.Scope scope = observation.openScope()) {
            response.setHeader("X-B3-TraceId", tracer.currentTraceContext().context().traceId());
            log.warn("Missing log correlation...");
            // ...
        }
    }

}

ServerHttpObservationFilter invoked observation.stop() in the finally block // org.springframework.web.filter.ServerHttpObservationFilter

protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    Observation observation = createOrFetchObservation(request, response);
    try (Observation.Scope scope = observation.openScope()) {
        filterChain.doFilter(request, response);
    } catch (Exception ex) {
        observation.error(unwrapServletException(ex));
        response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
        throw ex;
    } finally {
      // Only stop Observation if async processing is done or has never been started.
        if (!request.isAsyncStarted()) {
            Throwable error = fetchException(request);
            if (error != null) {
                observation.error(error);
            }
            observation.stop();
        }
    }
}

Sample: https://github.com/PerryZhao/spring-filter-error-lost-log-correlation-demo/tree/main 1. Start the application 2. Access http:localhost:8080/api/demo three times The output log is as follows:

2023-06-20T10:54:52.062+08:00  INFO [,649114fc015265da50f38998b7df244d,50f38998b7df244d] 70495 --- [nio-8080-exec-1] org.example.filter.MyTestFilter          : My filter
2023-06-20T10:54:52.077+08:00 DEBUG [,,] 70495 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : "ERROR" dispatch for GET "/error", parameters={}
2023-06-20T10:54:52.097+08:00  WARN [,,] 70495 --- [nio-8080-exec-1] o.example.exception.MyExceptionHandler   : Exception occurred.
2023-06-20T10:54:52.097+08:00  WARN [,649114fc015265da50f38998b7df244d,50f38998b7df244d] 70495 --- [nio-8080-exec-1] o.example.exception.MyExceptionHandler   : Scoped.
2023-06-20T10:54:52.114+08:00 DEBUG [,,] 70495 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Exiting from "ERROR" dispatch, status 400
2023-06-20T10:55:05.816+08:00  INFO [,649115091a05da24cbd6ec5bcfb4e1f5,cbd6ec5bcfb4e1f5] 70495 --- [nio-8080-exec-2] org.example.filter.MyTestFilter          : My filter
2023-06-20T10:55:05.817+08:00 DEBUG [,,] 70495 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet        : "ERROR" dispatch for GET "/error", parameters={}
2023-06-20T10:55:05.818+08:00  WARN [,,] 70495 --- [nio-8080-exec-2] o.example.exception.MyExceptionHandler   : Exception occurred.
2023-06-20T10:55:05.820+08:00 DEBUG [,,] 70495 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet        : Exiting from "ERROR" dispatch, status 400
2023-06-20T10:55:16.913+08:00  INFO [,64911514535e0aa94e10eb69074d6a82,4e10eb69074d6a82] 70495 --- [nio-8080-exec-3] org.example.filter.MyTestFilter          : My filter
2023-06-20T10:55:16.914+08:00 DEBUG [,,] 70495 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet        : "ERROR" dispatch for GET "/error", parameters={}
2023-06-20T10:55:16.915+08:00  WARN [,,] 70495 --- [nio-8080-exec-3] o.example.exception.MyExceptionHandler   : Exception occurred.
2023-06-20T10:55:16.915+08:00  WARN [,64911514535e0aa94e10eb69074d6a82,4e10eb69074d6a82] 70495 --- [nio-8080-exec-3] o.example.exception.MyExceptionHandler   : Scoped.
2023-06-20T10:55:16.916+08:00 DEBUG [,,] 70495 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet        : Exiting from "ERROR" dispatch, status 400

Comment From: bclozel

Closing in favor of the StackOverflow question.