Spring REST using Jackson - 400 bad request logging
Asked Answered
U

8

75

I have spring REST set up fine using Jackson/JSON and everything works.

But I knowingly introduced an error in the structure of the message which resulted in a 400 - Bad Request. But there was no log output on the server. The error I would be expecting would be something like "Jackson unknown property exception" or whatever but it was caught and a 400 error was sent to the client, but no log of the exception on the server.

I don't want to debug everything on the server clearly, but I want Spring network level exceptions like this clearly labelled as error.

What is the correct way to switch this on?

Thanks!

Unarmed answered 26/6, 2013 at 17:44 Comment(0)
M
64
@ExceptionHandler
@ResponseStatus(HttpStatus.BAD_REQUEST)
public void handle(HttpMessageNotReadableException e) {
    logger.warn("Returning HTTP 400 Bad Request", e);
}
Marabou answered 26/6, 2013 at 18:33 Comment(11)
Surely there is a more general approach to this? I would imagine the spring framework that throws these exceptions would have a log call before it throws the exception.Unarmed
Spring does log them at DEBUG level as far as I can remember. Do take note that you can place this exception handler method in a superclass. Plus if you ever want to return error messages to the client (in addition to the status code), this is the way to go.Marabou
Another option would be to implement your own ExceptionHandlerResolver to do the logging at a more "general" level. Have a look at static.springsource.org/spring/docs/current/…Marabou
I want to avoid writing any code. I'm new to the Spring framework but I'm going to just put debug level on for everything, and prune it down to what I need I think. Thanks a lot for your help.Unarmed
Thanks a ton @Jukka.. I was struggling to find the error in my JSONEcholalia
@Marabou As far as for Spring4, the log level is TRACE.Primalia
@StealthRabbi in your @Controller annotated class. Also see David Welch's alternative approach belowMarabou
-1 - random code stub without explaining anything about what you're trying to achieve. Where does this go? what does this do? pls explainCodel
Ambiguous @ExceptionHandler method mappedRives
Spring WebFlux users, handle ServerWebInputException instead.Radionuclide
I just added a debug breakpoint in my IDE on HttpMessageNotReadableException and inspect the error. Problem solved.Chorea
C
58

In case anyone else stumbles on this issue, the following worked for me in Spring Boot 2 / Spring 5 and didn't require a code change.

I set the log level for org.springframework.web.servlet.mvc.method.annotation to DEBUG. In my case I was seeing 400 responses in my client code but no logging on the server side (even with a custom error handler). After changing that log level the problem was obvious:

DEBUG 172.19.0.16  cs 2018-Jun-08 20:55:08.415 [https-jsse-nio-443-exec-9] - 
  method.annotation.RequestResponseBodyMethodProcessor[line ?] - 
  Read [class java.lang.String] as "application/xml;charset=UTF-8" with [org.springframework.http.converter.xml.MarshallingHttpMessageConverter@2de50ee4]
DEBUG 172.19.0.16  cs 2018-Jun-08 20:55:08.418 [https-jsse-nio-443-exec-9] - 
  method.annotation.ServletInvocableHandlerMethod[line ?] - 
  Failed to resolve argument 0 of type 'java.lang.String'
org.springframework.beans.TypeMismatchException: 
  Failed to convert value of type 'core.dto.RequestDTO' to required type 'java.lang.String'
Curiel answered 13/12, 2018 at 21:22 Comment(6)
This is the best option and should be accepted instead: you can keep the root log level to WARN while checking the request/reponse mapping/Jackson convertion details and failures, and no extra exception handler is required. Then you will not lose yourself upon the avalanche of log lines.Linhliniment
Could you please get me the configuration details to set it to DEBUG. How can I do it in log4j.properties.Electrojet
There are plenty of examples online about configuring log4j using properties file. You can try this article: journaldev.com/10698/log4j-properties-file-example. Basically you'll need something like log4j.logger.org.springframework.web.servlet.mvc.method.annotation=DEBUG, console but it will depend on what appenders you have defined.Curiel
With Spring Boot you can just add -Ddebug to your VM options.Calciferous
Or with SpringBoot just add an environment variable (e.g. for Kubernetes/OpenShift directly to the ConfigMap): LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_WEB_SERVLET_MVC_METHOD_ANNOTATION: DEBUGEnrapture
Can also do this programmatically, if you wanted to toggle it on and off when the instance is running ((LoggerContext) LoggerFactory.getILoggerFactory()).getLogger("org.springframework.web.servlet.mvc.method.annotation").setLevel(Level.DEBUG)Acquisition
T
49

Building on @Jukka's answer, you can enable this globally for all controllers using @ControllerAdvice (introduced in Spring 3.2). It does require a little code on your end, but in my experience you usually end up needing a global error handling configuration anyways and this allows you to set breakpoints / easily inspect the problems.

An example of this is below:

@ControllerAdvice
public class ControllerConfig {

    @ExceptionHandler
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public void handle(HttpMessageNotReadableException e) {
        log.warn("Returning HTTP 400 Bad Request", e);
        throw e;
    }
}
Teeth answered 20/7, 2014 at 20:33 Comment(5)
Very helpful. But be sure to place your ControllerAdvice inside a package scanned by Spring: my first attempt failed because I placed it - being a rather generic approach - in another, more general package than my controller and so it has not been heeded by Spring.Vervain
This doesn't seem to work anymore because Spring has an @ExceptionHandler annotation for HttpMessageNotReadableException in its ResponseEntityExceptionHandler#handleException method now. When I add the above to my code, I get this error: java.lang.IllegalStateException: Ambiguous @ExceptionHandler method mapped for [class org.springframework.http.converter.HttpMessageNotReadableException] because my exception mapper is conflicting with Spring's.Rive
Here's the documentation to Spring's exception handler that I mentioned above: docs.spring.io/spring-framework/docs/current/javadoc-api/org/…Rive
Spring WebFlux users, handle ServerWebInputException instead.Radionuclide
Ambiguous @ExceptionHandler method mapped for [class org.springframework.http.converter.HttpMessageNotReadableException I hate that part of spring, and the fact that they hide errors, Big anti pattern.Rives
S
14

You can set the log level to debug for this property in application.properties.

logging.level.org.springframework.web= DEBUG
Sofiasofie answered 5/1, 2022 at 11:15 Comment(4)
this also works with a reactive stack (webflux)Summersummerhouse
I did this and it did log the values of the DTO object but did not provide which property had trouble serializingRhythmics
Thanks. This actually showed the error in the terminal.Tyrothricin
This works simple and efficient.Graphemics
L
8

In my case, the problem was in my @ControllerAdvice-annoted class, which extended ResponseEntityExceptionHandler. As I got that handler code from some random tutorial about custom exception handling, I didn't bother to check what that superclass was doing (my custom exception handling worked just fine).

The problem is ResponseEntityExceptionHandler.handleException() handles MethodArgumentNotValidException along with other Spring MVC exceptions, delegating handling logic to specific handle- methods. As I did not provide an implementation for any of such methods (in this case handleMethodArgumentNotValid()) to return my own response body, I ended up with the default implementation that returns a response with null body :).

Solution

Override the methods you need or don't extend that class at all.

Check ResponseEntityExceptionHandler docs for the list of hanler- methods implemented.

Lustrum answered 6/11, 2019 at 0:27 Comment(0)
G
1

I don't think that it is mandatory to extend ResponseEntityExceptionHandler. You can create multiple handler methods in ControllerAdvice class annotated with ExceptionHandler and you can return any kind of response. Please check below link for more details and code example. https://www.thetechnojournals.com/2019/11/how-to-handle-exceptions-in-rest.html

Grind answered 6/11, 2019 at 6:35 Comment(0)
E
1

If you are extending ResponseEntityExceptionHandler, you can override handleExceptionInternal() in order to have any exception logged:

    @Override
    protected ResponseEntity<Object> handleExceptionInternal(Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {
        log.error("There was an error handling request", ex);
        return super.handleExceptionInternal(ex, body, headers, status, request);
    }

Edme answered 24/10, 2023 at 19:31 Comment(0)
N
0

I had a similar issue. I declared a REST method parameter to be required, and if I called the endpoint without passing in the parameter, it just gave me a 400 with Bad Request as error. I'm not sure, if you wan't to have a more clear error message on the client, too. If yes, you can do that by adding following to your application.yaml:

server:
  error:
    include-message: always

This results in something like

{
    "timestamp": "2024-04-09T14:14:31.504+00:00",
    "status": 400,
    "error": "Bad Request",
    "message": "Required parameter 'datum' is not present.",
    "path": "/rest/v1/xyz"
}

(fiy: it's said that sensitive information can be disclosured by doing this, so think about if it's okay :) )

Nilla answered 9/4 at 14:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.