I need to log trace information using a Tracer
bean, which is request-scoped, in both my REST method and exception handler. The Tracer
bean is consumed in the REST method to log the trace information at the start of processing the request, and I want to use the same trace message in the exception handler when an exception occurs.
However, I'm facing difficulties accessing the request-scoped Tracer
bean in my exception handler.
I have tried both rest controller @ExceptionHhandler
annotation in the REST controller and @ControllerAdvice
, but neither seams to support access to a scope bean.
Here's my setup:
@RestController
public class MyController {
@Bean
@RequestScope
Tracer tracer(){
return new Tracer();
}
@GetMapping(path = "list")
public String list(Tracer trace){
return trace.getCorrelationId();
}
@GetMapping(path = "throw")
public String throwEx(Tracer trace,HttpServletRequest req){
throw new RuntimeException("Ops");
}
@ExceptionHandler({Exception.class})
ResponseEntity<String> defaultExceptionHandler(Tracer tracer, HttpServletRequest req, Exception ex) {
return ResponseEntity
.internalServerError()
.body("ERROR: " + tracer.getCorrelationId());
}
}
The tracer (for completeness):
@Getter
public class Tracer {
private final String requestId;
private final String correlationId;
public Tracer() {
requestId = null;
correlationId = UUID.randomUUID().toString();
}
public Tracer(String requestId, String correlationId) {
this.requestId = requestId;
this.correlationId = correlationId;
}
}
Here the test:
@SpringBootTest
@AutoConfigureMockMvc
class MyControllerTest {
@Autowired
protected MockMvc mockMvc;
@Test
void testExceptionHandler() throws Exception {
mockMvc
.perform(MockMvcRequestBuilders.get("/throw")
)
.andDo(MockMvcResultHandlers.print())
.andReturn()
.getResponse()
.getContentAsString();
}
}
And here is the problem:
Could not resolve parameter [0] in org.springframework.http.ResponseEntity<java.lang.String> com.baeldung.scopes.MyController.defaultExceptionHandler(com.baeldung.scopes.Tracer,jakarta.servlet.http.HttpServletRequest,java.lang.Exception): No suitable resolver
It seems that I cannot directly inject the Tracer bean into the exception handler method.
I'm I missing something here? Is there another approach to do this problem?
Any help or suggestions would be greatly appreciated!
UPDATE: Most correct way is this
Maybe "dirty", but at least it works:
@Getter
public class YourException extends RuntimeException {
private final Tracer tracer;
//Your constructors
}
@GetMapping(path = "throw")
public String throwEx(Tracer trace,HttpServletRequest req){
throw new YourException("Ops", tracer); -- pass your tracer to the constructor
}
@ExceptionHandler({YourException.class})
ResponseEntity<String> defaultExceptionHandler(HttpServletRequest req, YourException ex) {
return ResponseEntity
.internalServerError()
.body("ERROR: " + ex.getTracer().getCorrelationId());
}
Other solution is ThreadLocal (spring web-mvc uses thread-per-request model)
public class TracerInfo {
private static final ThreadLocal<Tracer> TRACER = new ThreadLocal<>();
public static void set(Tracer tracer) {
TRACER.set(tracer);
}
public static Tracer get() {
return TRACER.get();
}
}
@GetMapping(path = "throw")
public String throwEx(Tracer trace,HttpServletRequest req) {
TracerInfo.set(tracer);
throw new YourException("Ops");
}
@ExceptionHandler({Exception.class})
ResponseEntity<String> defaultExceptionHandler(HttpServletRequest req, Exception ex) {
return ResponseEntity
.internalServerError()
.body("ERROR: " + TracerInfo.get().getCorrelationId());
}
Ps: fix compile error if any since I do not have IDE right now