Aspect to wrap around log entries - spring

I am using the sl4j Logger throughout my spring application.
I would like to append a custom string to all log entries (info/trace/debug etc) done throughout the application. This would ideally be done using an Aspect however I am not sure what format the aspect would have. I am assuming something along the lines of
#Around("org.slf4j.Logger.info(*)")
however i cannot find any reliable way to make this apply to all the methods involved correctly.

As #PavelHoral mentioned The following was the solution using MDC
Create your own filter
import org.slf4j.MDC;
import javax.servlet.*;
import java.io.IOException;
public class MDCLoggingFilter implements Filter {
#Override
public void init(final FilterConfig filterConfig) throws ServletException {}
#Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException {
MDC.put("myKey", "myValue");
try {
chain.doFilter(request, response);
} finally {
// remove the key once you are done with it
MDC.remove("myKey");
}
}
#Override
public void destroy() {}
}
Then add your filter to your web.xml as follows
<filter>
<filter-name>mdcLoggingFilter</filter-name>
<filter-class>path.to.MDCLoggingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>mdcLoggingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
However, this does not seem to be applying to all my messages. some warnings seem to not be displaying the field. currently investigating.

Related

Logging all request and response in Spring Boot REST service

I use Spring boot and have some REST controllers. I want to logging all request and response. I using external tomacat, not embeded! I write Interceptor:
#Component
#Log4j2
public class LoggingWebMvcInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
final ContentCachingRequestWrapper wrapper = new ContentCachingRequestWrapper(request);
log.debug("REQ!!!! {}", wrapper.getReader().lines().collect(Collectors.joining(System.lineSeparator())));
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
//log.debug("Response: {}", response);
}
And adding his:
#Configuration
public class WebMvcConfig implements WebMvcConfigurer {
private final LoggingWebMvcInterceptor loggingWebMvcInterceptor;
#Autowired
public WebMvcConfig(LoggingWebMvcInterceptor loggingWebMvcInterceptor) {
this.loggingWebMvcInterceptor = loggingWebMvcInterceptor;
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loggingWebMvcInterceptor);
}
}
But It don't work!
When I try POST request, his logging, but I have error: "Required request body is missing.
What am I doing wrong? I created a wrapper for the request!
I need to completely log all requests (POST, GET, DELETE, PUT) with headers and body and all responses. How can i do this? Any help, please.
Although your problem is not every well understood (not documented well -- for example where this is coming from is not shown Required request body is missing.) but anyways.
For logging purposes, I would not go with an Interceptor as I feel that this is too much work. Instead you could very well create an Aspect with a pointcut defined to around methods annotated with the various Spring controller annotation. The ProceedingJoinPoint#proceed method effectively allows you to grab the response object and the request itself contains all the information needed regarding parameters, IP, methods and so on.
With that in hand, you could then inject a HttpServletRequest in there, thus ending up having all the right tools to perform any logging activities.
Adding the caching wrapper is something very correct indeed if you would like to cache and re-read the HttpServletRequest's body multiple time but I would avoid adding it in the Interceptor/Aspect itself.
According to Baeldung documentation, ContentCachingRequestWrapper class has these limitations:
ContentCachingRequestWrapper class only supports the following:
Content-Type:application/x-www-form-urlencoded
Method-Type:POST
and
We must invoke the following method to ensure that request data is cached in ContentCachingRequestWrapper before using it: requestCacheWrapperObject.getParameterMap();
https://www.baeldung.com/spring-http-logging
You can use a web Filter (javax.servlet.Filter) as :
public class CustomFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain){
//Log actions heres
chain.doFilter(req, resp);}}
Then declare your filter in web.xml as :
<filter>
<filter-name>MyFilter</filter-name>
<filter-class>package.CustomFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>MyFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

Spring HttpServletRequest unaccessible in HystrixCommand

Inside a Javanica annotated #HystrixCommand we are checking if the request was in an actual HTTP servlet request by checking:
RequestContextHolder.getRequestAttributes() != null;
However invoked from a #HystrixCommand this condition is always false, even if the request came from a Spring MVC request.
If I remove the #HystrixCommand annotation everything works fine.
We also tried to use the HttpServletRequest directly, this works fine (without #HystrixCommand):
LOGGER.info(request.getHeader("X-Client"));
With annotated #HystrixCommand we are facing exception indicating I am not in an valid HttpServletRequest. I know it is due to Hystrix running commands in separate Threads from its own ThreadPool and tried to do this, but doesn't work either:
public class RequestServletFilter implements Filter {
#Override
public void init(FilterConfig filterConfig) throws ServletException {
// No Impl
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HystrixRequestContext context = HystrixRequestContext.initializeContext();
try {
chain.doFilter(request, response);
} finally {
context.shutdown();
}
}
#Override
public void destroy() {
// No Impl
}
Does someone have a clue how to delegate the Spring HttpServletRequest into HystrixCommands?
Any help is appreciated.
When using the RequestContextHolder by default it parameters are not shared (for good reasons!).
Assuming that you are using a DispatcherServlet to handle your request you can set its [threadContextInheritable] to true to have the RequestContext and LocaleContext shared between requests.
The same applies for the RequestContextFilter, it isn't possible with the RequestContextListener.
Note: I would consider sharing the HttpServletRequest between threads as something you shouldn't be doing and should be done with great care!

SpringMVC Session Timeout - Redirect to a Special JSP

I've looked everywhere but haven't found a simple solution.
We have a special JSP, timeout.jsp, that needs to be shown whenever a SpringMVC module intercepts an invalid session action. The timeout is already configured in web.xml and works correctly.
Previously in Struts, it was a matter of defining a forward and intercepting dispatchMethod,
<forward name="sessionTimeout" path="/WEB-INF/timeout.jsp" redirect="false" />
#Override
protected ActionForward dispatchMethod(final ActionMapping mapping, final ActionForm form,
final HttpServletRequest request, final HttpServletResponse response, final String name)
throws Exception {
//...
if (!isSessionValid())
return mapping.findForward("sessionTimeout");
}
But how would you implement a catch-all solution in SpringMVC modules?
All my SpringMVC URLs come to this servlet mapping, *.mvc:
<servlet-mapping>
<servlet-name>mvc-dispatcher</servlet-name>
<url-pattern>*.mvc</url-pattern>
</servlet-mapping>
Anything that sends a URL with this pattern should be cross-checked for session validity and if invalid, redirected to timeout.jsp.
NOTE
The solution given here (https://stackoverflow.com/a/5642344/1005607) did not work:
<web-app>
<error-page>
<exception-type>org.springframework.web.HttpSessionRequiredException</exception-type>
<location>/index.jsp</location>
</error-page>
</web-app>
There's a NullPointerException in my SpringMVC Form Code even before any kind of SessionRequiredException, as soon as I try to access the session. I need to globally protect against these NullPointerExceptions.
My final solution: an old-fashioned Filter. It works for me, no other simple solution available.
web.xml
<filter>
<filter-name>spring_mvc_controller_filter</filter-name>
<filter-class>myapp.mypackage.SpringMVCControllerFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>spring_mvc_controller_filter</filter-name>
<url-pattern>*.mvc</url-pattern>
</filter-mapping>
SpringMVCControllerFilter
public class SpringMVCControllerFilter implements Filter
{
#Override
public void destroy() {
// TODO Auto-generated method stub
}
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpSession session = request.getSession(false);
if (session.isValid() && !session.isNew())
{
chain.doFilter(request, response);
}
else
{
request.getRequestDispatcher("/WEB-INF/jsp/sessionTimeout.jsp").forward(request, response);
}
}
#Override
public void init(FilterConfig arg0) throws ServletException {
// TODO Auto-generated method stub
}
}

understanding spring filters in Spring 3.2.8

I am implementing a filter for security reasons.... The point that the page gets frozen and I don't know exactly why because the filter in fact is not still doing anything !
<!-- spring security csrf -->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>fr.telecom.support.context.DevicesSecurityFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Here my filter:
public class DevicesSecurityFilter extends DelegatingFilterProxy {
public DevicesSecurityFilter() {
// TODO Auto-generated constructor stub
}
public DevicesSecurityFilter(Filter delegate) {
super(delegate);
}
public DevicesSecurityFilter(String targetBeanName) {
super(targetBeanName);
}
public DevicesSecurityFilter(String targetBeanName,
WebApplicationContext wac) {
super(targetBeanName, wac);
}
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
System.out.println ("do Filter...");
//super.doFilter(request, response, filterChain);
}
}
The filter is doing something: it prevents the request from going to the next filter/servlet in the chain, and doesn't send anything to the response. So it basically intercepts all requests and responds with a blank response to all of them.
For the filter to be "transparent", its doFilter() method must contain
filterChain.doFilter(request, response);
or, since it's a DelegatingFilterProxy, it shouldn't have any doFilter() method at all, and instead let the parent's doFilter method implementation do its job: delegating to the Spring bean it's configured to use. In fact, you shouldn't even create subclasses of DelegatingFilterProxy: as its name indicates, it works, on its own, by delegating to a Spring bean. The Spring bean should be the one doing the filtering job.
By overriding the doFilter() method, you're preventing that delegation to happen.

init-param in web.xml for Spring OncePerRequestFilter not getting set

I have a Spring MVC app with Spring 3.2.5 that has a filter on it that makes a change to a request. This filter has a parameter that I am trying to set via the init-param in web.xml. I cannot seem to get the web.xml to set the value on my bean, and cannot figure out where it is getting stored (if it is at all) in the spring bean. I have not added my filter bean to my context, but only to my web.xml.
My web.xml:
<filter>
<filter-name>myFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<description>
...
</description>
<param-name>allowMethods</param-name>
<param-value>GET, POST</param-value>
</init-param>
</filter>
My filter code:
public class MyFilter extends OncePerRequestFilter {
private static final Logger logger = LoggerFactory.getLogger(MyFilter.class);
protected String allowMethods;
#Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// Do filter stuff, and try printing allowMethods via the logger
}
/**
* #return the allowMethods
*/
public String getAllowMethods()
{
return allowMethods;
}
/**
* #param allowMethods the allowMethods to set as a comma separated list
* for request types. For example, "GET" or "GET, PUT", or "GET, PUT, POST, DELETE"
*/
public void setAllowMethods(String allowMethods)
{
logger.info("Setting CORS filter to allow cross-site scripting with methods: {}", allowMethods);
this.allowMethods = allowMethods;
}
}
I do not see the setter message, the value is not stored in the filter configuration that Spring saves, and I cannot override any methods that look like they'll give me the information I want (they are declared final).
How do I get the init-param mapped to my filter. Do I have to use the applicationContext to do it? If so, what is the convention to do this with Spring?
Thanks

Resources