Imagine we have an web SpringBoot application containing a Controller. It returns a template index containing two links: on css file and on js file.
So we want to log every request from client while opening index.html
:
index.html
test.css
test.js
We must set up the logging of our classes. Take a look at example of logback-spring.xml.
Every Spring Web application have a chain on different filters. Every request flows through this filter chain before getting a controller.
So we can implement a logging filter and put it into Spring Filter Chain to achieve our goal.
@Slf4j
@Component
public class RequestLoggingFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
log.debug(
String.format("FILTERED URL: %s", request.getRequestURI())
);
//continue filtering
filterChain.doFilter(request, response);
}
}
Using @Slf4j
annotation get us to decrease boilerplate code to have a logger in a class.
We must annotate our class with @Component
annotation to make SpringBoot to instantiate this class as spring bean in Singleton scope.
Our class extends OncePerRequestFilter
abstract class so every request goes through our filter only once.
And we are able to log anything regarding the request inside doFilterInternal
method.
You may run testControllerLoggingWithFilter to see the result
@Test(expected = HttpClientErrorException.NotFound.class)
public void testControllerLoggingWithFilter() {
restTemplate.getForObject("http://localhost:"+port+"/", String.class);
restTemplate.getForObject("http://localhost:"+port+"/test.js", String.class);
restTemplate.getForObject("http://localhost:"+port+"/test.css", String.class);
restTemplate.getForObject("http://localhost:"+port+"/does-not-exist.file", String.class);
}
So our goal is achieved. We can see all files we request and not existing file too.
Handler interceptors have more power to process your requests since they handle not the fact of request but its events: before request, after request, after completion.
You can check out Spring class org.springframework.web.servlet.handler.HandlerInterceptorAdapter
to see its methods we are able to implement in our handler.
Lets our handler implement preHandle
and postHandle
events:
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.debug(
String.format("HANDLER(pre) URL: %s", request.getRequestURI())
);
return super.preHandle(request, response, handler);
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.debug(
String.format("HANDLER(post) URL: %s", request.getRequestURI())
);
super.postHandle(request, response, handler, modelAndView);
}
But the handler will not work until we actually tell Spring to use it. We must build a configuration to enable our handler.
@Configuration
public class WebApplicationConfiguration implements WebMvcConfigurer {
@Setter(onMethod_ = @Autowired)
private RequestLoggingHandler requestLogger;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(requestLogger);
}
}
Lets start our test to check out the result!
Spring provides a mechanism to log every request out of the box - CommonsRequestLoggingFilter
bean.
You can implement an instance of this class ...
@Bean
public CommonsRequestLoggingFilter logFilter() {
CommonsRequestLoggingFilter filter = new CommonsRequestLoggingFilter();
filter.setIncludeQueryString(true);
filter.setIncludePayload(true);
filter.setMaxPayloadLength(10000);
filter.setIncludeHeaders(false);
filter.setAfterMessagePrefix("REQUEST DATA : ");
return filter;
}
... and then set up logging level into logback-spring.xml
located into resources
or providing it externally ...
<logger name="org.springframework.web.filter.CommonsRequestLoggingFilter">
<level value="DEBUG" />
</logger>
... to enable requests logging.
Starting our test we have the result: