Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get stack trace of Exception through spring rest template

I have 2 services - Service 1 and Service 2. Service 1 calls some Service 2 API through Spring Rest Template. Now Some exception happened in Service 2. I need the whole stack trace of it in Service 1. How to get it ?

Service 1  ---calls--> Service 2

Does the stack trace even get passed to Service 1 by Spring ?

You can say I am calling like this :

HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
HttpEntity<String> entity = new HttpEntity<String>(headers);
return restTemplate.exchange("http://localhost:8080/products", HttpMethod.GET, entity, String.class).getBody();
like image 494
Number945 Avatar asked Mar 16 '19 16:03

Number945


People also ask

How do I print a stack trace for exceptions?

Using printStackTrace() method − It print the name of the exception, description and complete stack trace including the line where exception occurred. Using toString() method − It prints the name and description of the exception. Using getMessage() method − Mostly used. It prints the description of the exception.

How do you handle exceptions in RestTemplate?

Default Error Handling By default, the RestTemplate will throw one of these exceptions in the case of an HTTP error: HttpClientErrorException – in the case of HTTP status 4xx. HttpServerErrorException – in the case of HTTP status 5xx. UnknownHttpStatusCodeException – in the case of an unknown HTTP status.

How do you handle exceptions in spring boot REST API?

Altogether, the most common way is to use @ExceptionHandler on methods of @ControllerAdvice classes so that the exception handling will be applied globally or to a subset of controllers. ControllerAdvice is an annotation introduced in Spring 3.2, and as the name suggests, is “Advice” for multiple controllers.

How is stack trace generated?

The stack trace contains all invocations from the start of a thread until the point it's generated. This is usually a position at which an exception takes place. When printed out, the generation point shows up first, and method invocations leading to that point are displayed underneath.


5 Answers

I need the whole stack trace of it in Service 1. How to get it ?

So there are ways to get it , in essence you have to implement. You can get your relevant exception message/trace in JSON response from Service 2 . That is , when there is any exception at Service 2 end then , we can configure response to send relevant exception information.

In this post there are 3 answers explaining different ways to achieve, also this one. Now on :

Does the stack trace even get passed to Service 1 by Spring ?

Normally any unhandled/runtime exception thrown when processing a web-request causes the server to return an HTTP 500 response.

So the answer is spring does not transfer the stack trace to Service 1 rather respond with error HTTP 500 and the most likely message of your exception.

However, any exception that you write yourself can be annotated with the @ResponseStatus annotation (which supports all the HTTP status codes defined by the HTTP specification).

When an annotated exception is thrown from a controller method, and not handled elsewhere, it will automatically cause the appropriate HTTP response to be returned with the specified status-code and with the message/trace written. For example,

@ResponseStatus(value=HttpStatus.NOT_FOUND, reason="No such Account")  // 404
public class AddressNotFoundException extends RuntimeException {
    // ...
}

And here is a controller method using it:

@RequestMapping(value="/account/{id}", method=GET)
public String showOrder(@PathVariable("id") long id, Model model) {
    Account account = accountServices.findAccountById(id);

    if (account == null) throw new AddressNotFoundException(id);
    model.addAttribute(account);
    return "accountDetail";
}

A familiar HTTP 404 response will be returned if the URL handled by this method includes an unknown account id.

Hope this helps.

like image 129
Vivek Avatar answered Oct 08 '22 20:10

Vivek


To summarize a straight-forward approach it is not much more than adding Spring Boot's Zipkin and Sleuth starters into your pom.xmls to enable log tracing in both directions between multiple apps…

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-sleuth</artifactId>
            <version>${spring-cloud-sleuth.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-sleuth</artifactId>
    </dependency>
</dependencies>
<dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>

…and configure your log pattern in your application.properties to reflect the SpanIds and TraceIds which Sleuth injected in your requests…

logging.pattern.level=[%X{X-B3-TraceId}/%X{X-B3-SpanId}] %-5p [%t] %C{2} - %m%n

You can also compare your own progress with a running example on Openzipkin's Github account.

Maybe you could give it a try and show us your experiences and progress with it!

like image 22
mle Avatar answered Oct 08 '22 19:10

mle


1) Catch specific HttpServerErrorException and HttpClientErrorException before Exception, propagate exception object all the way up to service 1 or resource.

try {
 //code
} catch (HttpServerErrorException | HttpClientErrorException ex) {
throw new SystemException("Http Exception", ex.getResponseBodyAsString(), 
ex);
} catch (RuntimeException ex) {
throw new SystemException("RuntimeException", ex);
} catch (Exception ex) {
throw new SystemException("default exception block", ex);
}

2) Have Exception mapper like below for your system and business exceptions where all the error messages can be caught and logged.

   public class SystemException extends Throwable implements
        ExceptionMapper<Throwable> {

      @Override
      public Response toResponse(Throwable ex) {
         //code to handle exception
      }
like image 43
Anil Konduru Avatar answered Oct 08 '22 19:10

Anil Konduru


Try to convert that exception in Service1 into string and attach that exception string to response with an error code, try to catch that exception in your Service2 By using the below code u can convert exception to string

import java.io.StringWriter;
import java.io.PrintWriter;

StringWriter stringWriter = new StringWriter();
PrintWriter printWriter = new PrintWriter(stringWriter);
e.printStackTrace(printWriter);
String exceptionString = stringWriter.toString();
System.out.println(exceptionString); 

In your Catch block of service1

return ResponseEntity.badRequest()
            .body(exceptionString);

or

return new ResponseEntity<>(
      exceptionString, 
      HttpStatus.BAD_REQUEST);
like image 37
Bhaskara Arani Avatar answered Oct 08 '22 20:10

Bhaskara Arani


when service 2 get exception it will return http 500 so you cant see any detail on service 1 , so you have to intercept exception on @controlleradvice , create a controller advice and set runtime or any exception with specific http status and stack trace as error detail return it .

@ControllerAdvice
    public class RestResponseEntityExceptionHandler 
      extends ResponseEntityExceptionHandler {

        @ExceptionHandler(value 
          = { Service2Exception.class})
        protected ResponseEntity<Service2Error> handleService2Exception(
          RuntimeException ex, WebRequest request) {
            String bodyOfResponse = "This should be application specific";
             Service2Error error = new Service2Error();
              error.setStack(toStack(ex));
            return new ResponseEntity<Server2Error>(error,HttpStatus.NOT_FOUND);// what you want
        }
    }
        public static String toStack(Exception e) {
             StringWriter stringWriter = new StringWriter();
             PrintWriter printWriter = new PrintWriter(stringWriter);
             e.printStackTrace(printWriter);
             String exceptionString = stringWriter.toString();
             return exceptionString;
       }
}


      public class Service2Error {
                private String errorStack;
                private int errorCode;
                         // getter settter


       }
like image 1
Mithat Konuk Avatar answered Oct 08 '22 20:10

Mithat Konuk