151 lines
5.4 KiB
Markdown
151 lines
5.4 KiB
Markdown
# How to log every Request in SpringBoot
|
|
|
|
## Prerequesites
|
|
|
|
Imagine we have an web SpringBoot application containing a [Controller](./src/main/java/com/bvn13/example/springboot/springrequestlogger/controllers/FirstController.java).
|
|
It returns a template [index](/src/main/resources/templates/index.html) containing two links: on [css file](./src/main/resources/static/test.css) and on [js file](./src/main/resources/static/test.js).
|
|
|
|
So we want to log every request from client while opening `index.html`:
|
|
1. `index.html`
|
|
2. `test.css`
|
|
3. `test.js`
|
|
|
|
## Enabling logging
|
|
|
|
We must set up the logging of our classes. Take a look at example of [logback-spring.xml](./src/main/resources/logback-spring.xml).
|
|
|
|
## Build a Filter
|
|
|
|
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](./src/main/java/com/bvn13/example/springboot/springrequestlogger/filters/RequestLoggingFilter.java) and put it into Spring Filter Chain to achieve our goal.
|
|
|
|
```java
|
|
@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.
|
|
|
|
### Result
|
|
|
|
You may run [testControllerLoggingWithFilter](./src/test/java/com/bvn13/example/springboot/springrequestlogger/SpringrequestloggerApplicationTests.java) to see the result
|
|
|
|
```java
|
|
@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);
|
|
}
|
|
```
|
|
|
|
![](./img/2019-09-29_21-09.png)
|
|
|
|
So our goal is achieved. We can see all files we request and not existing file too.
|
|
|
|
|
|
## Building a Handler Interceptor
|
|
|
|
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](./src/main/java/com/bvn13/example/springboot/springrequestlogger/handlers/RequestLoggingHandler.java) implement `preHandle` and `postHandle` events:
|
|
|
|
```java
|
|
@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](./src/main/java/com/bvn13/example/springboot/springrequestlogger/handlers/WebApplicationConfiguration.java) to enable our handler.
|
|
|
|
```java
|
|
@Configuration
|
|
public class WebApplicationConfiguration implements WebMvcConfigurer {
|
|
|
|
@Setter(onMethod_ = @Autowired)
|
|
private RequestLoggingHandler requestLogger;
|
|
|
|
@Override
|
|
public void addInterceptors(InterceptorRegistry registry) {
|
|
registry.addInterceptor(requestLogger);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Result
|
|
|
|
Lets start our test to check out the result!
|
|
|
|
![](./img/2019-09-30_23-25.png)
|
|
|
|
|
|
## Spring way to log requests
|
|
|
|
Spring provides a mechanism to log every request out of the box - `CommonsRequestLoggingFilter` bean.
|
|
|
|
You can implement an [instance of this class](./src/main/java/com/bvn13/example/springboot/springrequestlogger/config/RequestLoggingFilterConfig.java) ...
|
|
|
|
```java
|
|
@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`](./src/main/resources/logback-spring.xml) located into `resources` or providing it externally ...
|
|
|
|
```xml
|
|
<logger name="org.springframework.web.filter.CommonsRequestLoggingFilter">
|
|
<level value="DEBUG" />
|
|
</logger>
|
|
```
|
|
|
|
... to enable requests logging.
|
|
|
|
### Result
|
|
|
|
Starting our test we have the result:
|
|
|
|
![](./img/2019-09-30_23-40.png)
|