Almas Abdrazak
Almas Abdrazak

Reputation: 3592

Log http request body , if exception has occured

I have been developing rest api service using spring boot. This is my rest controller

@RequestMapping(value = "owner/login", method = RequestMethod.POST, produces = "application/json;charset=UTF-8")
    @ResponseBody
    public ResponseEntity<String> login(@RequestBody Owner owner) throws TimeoutException, SocketTimeoutException, SocketException {
        controller.login(owner);
        return ResponseEntity.ok("\"error\": 0");
    }

This is controller

 public JsonObject login(Owner loginOwner){
        Map<String,String> params = new HashMap<String,String>();
        params.put("email", loginOwner.getEmail());
        params.put("password", loginOwner.getPassword());
        if(GMoikaStringUtils.isEmpty(loginOwner.getEmail()) || !GMoikaStringUtils.isValidEmail(loginOwner.getEmail())){
            throw new InvalidUserInputException("Wrong email format", CLASS_NAME, "login", params, "owners/owner/login POST");
        }
        else{
            someLogic...
        }
    }

The question is, using this structure i should to create map with params inside each method.How to avoid creation of map inside each method and put all params into this map? I need "MAP" to show params in logs in case if exception is occured. Maybe spring boot can do it , but i can't find my case in official docs. Thanks in advance

Upvotes: 3

Views: 2448

Answers (2)

Dimitri Mestdagh
Dimitri Mestdagh

Reputation: 44665

If you only want to do this after an InvalidUserInputException is thrown, you could write an exception handler:

@ExceptionHandler(InvalidUserInputException.class)
@ResponseBody
public ValidationError getError(InvalidUserInputException ex) {
    // ...
}

Since InvalidUserInputException is a custom exception, you could add a field of type Object in which you can pass your request body, for example:

throw new InvalidUserInputException("Wrong email format", loginOwner);

Now you can use the toString() method of the Owner object to log it. If you want to get it in a JSON style, you can use Apache commons and their ToStringBuilder:

@Override
public String toString() {
    return new ToStringBuilder(this, ToStringStyle.JSON_STYLE)
        .append("email", email)
        .append("password", password)
        .toString();
}

Alternatively, if you don't want to rely on using the toString() method and just want to return the plain request body, you can also add HttpServletRequest as a parameter to an exception handler.

Before you do that, you have to wrap it though, because the request body can only be read once, and to overcome that problem you should "cache" the request body once it's read. To do this, you can use Spring their ContentCachingRequestWrapper.

To wrap it, you should write your own Filter. You can do this by extending from Spring their OncePerRequestFilter like this:

ContentCachingRequestWrapper wrapper = (ContentCachingRequestWrapper) request;
wrapper.getContentAsByteArray();

@Component
public class RequestWrapperFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        filterChain.doFilter(new ContentCachingRequestWrapper(httpServletRequest), httpServletResponse);
    }
}

Now you can get the request body in the exception handler by calling the getContentAsByteArray() method. To get the request body as a String you can use IOUtils from Apache Commons:

ContentCachingRequestWrapper wrapper = (ContentCachingRequestWrapper) request;
logger.warn("Input validation failed, request body: {}", IOUtils.toString(wrapper.getContentAsByteArray(), wrapper.getCharacterEncoding()));

This will log the entire request body as a string. Be careful with logging sensitive information though.

Upvotes: 2

mohit sharma
mohit sharma

Reputation: 1070

You can use any Logger, like Log4j and log in else statement:

Steps to follow:
1. Download latest log4j distribution.
2. Add log4j’s jar library into your program’s classpath.
3. Create log4j’s configuration.
4. Initialize log4j with the configuration.
5. Create a logger.
6. Put logging statements into your code.

 Logger logger = Logger.getLogger(MyClass.class);

public JsonObject login(Owner loginOwner){
    Map<String,String> params = new HashMap<String,String>();
    params.put("email", loginOwner.getEmail());
    params.put("password", loginOwner.getPassword());
    if(GMoikaStringUtils.isEmpty(loginOwner.getEmail()) || !GMoikaStringUtils.isValidEmail(loginOwner.getEmail())){
        throw new InvalidUserInputException("Wrong email format", CLASS_NAME, "login", params, "owners/owner/login POST");
    }
    else{
        logger.info("Log this event");
    }
}

Refer here: http://www.codejava.net/coding/how-to-configure-log4j-as-logging-mechanism-in-java

Upvotes: 1

Related Questions