In spring boot 2 whats the most efficient way to log body? - java

I used to log my requests in filters in JAX-RS using this
#Provider
public class RequestLoggingFilter implements ContainerRequestFilter {
#Context
private HttpServletRequest servletRequest;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
BufferedInputStream stream = new
BufferedInputStream(requestContext.getEntityStream());
String payload = IOUtils.toString(stream, "UTF-8");
requestContext.setEntityStream(IOUtils.toInputStream(payload, "UTF-8"));
}
}
I am trying do something similar in spring boot 2 and I have tried using filters, Interceptors and also actuator. I lost the body as soon as I read it. In spring boot 2 the actuator's trace does not support logging of the body so is there any other way to log the request and response body without losing it?

You might find everything you need there: http://www.baeldung.com/spring-http-logging
Spring provides a built-in solution to log payloads. We can use
ready-made filters by plugging into Spring application using
configuration.
AbstractRequestLoggingFilter is a filter which provides basic
functions of logging. Subclasses should override the beforeRequest()
and afterRequest() methods to perform the actual logging around the
request.
Spring framework provides three concrete implementation classes which
can be used to log the incoming request. These three classes are:
CommonsRequestLoggingFilter Log4jNestedDiagnosticContextFilter
(deprecated) ServletContextRequestLoggingFilter Now, let’s move on to
the CommonsRequestLoggingFilter and configure it to capture incoming
request for logging.

Related

How can i achieve req id for tracing using springboot microservices?

The ask is:
Whenever a client calls API's, i want to tag it with a unique identifier or use one supplied by the client (usually in a query param) and pass it across components until that request is fulfilled sucessfully or fails. The goal is to get a holistic picture of how a request was handled by different components and what happened at each component and quickly identify issues.
How can i achieve this using springboot microservices. please help me.
Spring Cloud Sleuth is what you are looking for: https://cloud.spring.io/spring-cloud-sleuth/reference/html/
Spring Cloud Sleuth’s solution is to inject span and trace IDs into log entries. A trace ID is a unique identifier that an entire request flow will share. IA span is more local and is defined for each request received for each request sent event. They define particular interaction points.
The initial span, or root span, is generated when a client request is received from outside the distributed system. This request lacks trace and span information. The root span becomes the trace ID for the rest of the request flow through the system / systems.
The diagram below shows how Sleuth span and trace generation would work through a hypothetical service network.
All you need to do in your code is to add the dependency spring-cloud-starter-sleuth and Spring will automatically instrument the following communication channels:
requests over messaging technologies like Apache Kafka or RabbitMQ
HTTP headers received at Spring MVC controllers
requests made with the RestTemplate
If you want to start simple you could define a filter (e.g. by extending OncePerRequestFilter) that generates/extracts a request ID. You can also put it into Logback's MDC so that it is included in every logging statement that is issued from the thread executing the request (if configured):
#Component
public class RequestIdFilter extends OncePerRequestFilter {
private final ThreadLocal<String> requestId = new ThreadLocal<>();
public Optional<String> getCurrentRequestId() {
return Optional.ofNullable(requestId.get());
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
try {
requestId.set(UUID.randomUUID().toString()); // or extract from request
MDC.put("requestId", requestId.get());
chain.doFilter(request, response);
} finally {
requestId.remove();
}
}
}

Logging a request header before Spring Security filter chain

I want to log the contents of a given incoming request header as early as possible.
I know about approaches like CommonsRequestLoggingFilter or a logging HandlerInterceptor, however these seem to only log after Spring has executed a lot of other code, such as the Spring Security filter chain.
I want to log before Spring has done any of that, as early as possible based on a single requirement: the log message needs to be able to extract a header from the HTTP request.
Is there a way to do this?
I have found a way to do this using the embedded Tomcat. Since this receives the request before Spring does, you can capture the entire dispatched request from here.
public class CustomLoggerValve extends ValveBase {
private static final Logger logger = LoggerFactory.getLogger(CustomLoggerValve.class);
#Override
public void invoke(Request request, Response response) throws IOException, ServletException {
try {
MDC.put("requestId", UUID.randomUUID().toString());
logger.info("Received request");
getNext().invoke(request, response);
} finally {
MDC.remove("requestId");
}
}
}
Since I'm using Spring without Spring Boot, I can just add this to my Tomcat directly:
Tomcat tomcat = // ... your tomcat setup
tomcat.getService().getContainer().getPipeline().addValve(new CustomLoggerValve());
I haven't tried, but it looks like you could add this quite easily in Spring Boot too.
Presumably a similar approach would work with embedded Jetty/other JVM web servers.

HTTP Response Exception Handling in Spring 5 Reactive

I'm developing some reactive microservices using Spring Boot 2 and Spring 5 with WebFlux reactive starter.
I'm facing the following problem: I want to handle all HTTP Statuses that I receive from calling another REST Services and throws an exception when I receive some bad HTTP Status. For example, when I call an endpoint and I receive an 404 HTTP Status, I want to throw an exception and that exception to be handled in some ExceptionHandler class, just like the way it was in Spring 4 with #ControllerAdvice.
What is the right way to do this? Hope to receive some good suggestions.
This can be addressed in two independent parts.
How to convert HTTP 404 responses received by WebClient into custom exceptions
When using WebClient, you can receive HTTP 404 responses from remote services. By default, all 4xx and 5xx client responses will be turned into WebClientResponseException. So you can directly handle those exceptions in your WebFlux app.
If you'd like to turn only 404 responses into custom exceptions, you can do the following:
WebClient webClient = //...
webClient.get().uri("/persons/1")
.retrieve()
.onStatus(httpStatus -> HttpStatus.NOT_FOUND.equals(httpStatus),
clientResponse -> Mono.error(new MyCustomException()))
.bodyToMono(...);
This is obviously done on a per client call basis.
You can achieve the same in a more reusable way with an ExchangeFilterFunction that you can set once and for all on a WebClient instance like this:
WebClient.builder().filter(myExchangeFilterFunction)...
How to handle custom exceptions in WebFlux apps
With Spring WebFlux with annotations, you can handle exceptions with methods annotated with #ExceptionHandler (see Spring Framework reference documentation).
Note: using a WebExceptionHandler is possible, but it's quite low level as you'll have no high-level support there: you'll need to manually write the response with buffers without any support for serialization.
I think what you are looking for is WebFluxResponseStatusExceptionHandler the check this for reference.
In the WebHandler API, a WebExceptionHandler can be used to to handle
exceptions from the chain of WebFilter's and the target WebHandler.
When using the WebFlux Config, registering a WebExceptionHandler is as
simple as declaring it as a Spring bean, and optionally expressing
precedence via #Order on the bean declaration or by implementing
Ordered.
This example may help, have not tried it myself.
#Component
#Order(-2)
class RestWebExceptionHandler implements WebExceptionHandler{
#Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
if (ex instanceof PostNotFoundException) {
exchange.getResponse().setStatusCode(HttpStatus.NOT_FOUND);
// marks the response as complete and forbids writing to it
return exchange.getResponse().setComplete();
}
return Mono.error(ex);
}
}
class PostNotFoundException extends RuntimeException {
PostNotFoundException(String id) {
super("Post:" + id + " is not found.");
}
}

How to use Jersey's internal routing mechanism to extract a class/method reference?

I have a Jersey 1.8 application running. Jersey is running as a Servlet.
I need to write a servlet filter that given a plain request/response, is able to figure out which REST resource/method will respond to the request and extract values from annotations.
For example, imagine I have the following resource:
#Path("/foo")
#MyAnnotation("hello")
public class FooResource {
#GET
#Path("/bar")
#MyOtherAnnotation("world")
public Response bar(){
...
}
}
When a request GET /foo/bar comes in, I need my servlet filter to be able to extract the values "hello" and "world" from MyAnnotation and MyOtherAnnotation before Jersey's own servlet processes the request.
This filter logic should be able to work for all requests and all resources registered.
Is there a way to access Jersey's internal routing mechanism to obtain a class/method reference where Jersey will dispatch the request?
I'm open to other suggestions as well, but ideally nothing like trying to hack my own routing mechanism by reading the #Path annotations myself.
#Provider
#Priority(Priorities.AUTHORIZATION)
public class MyFilter implements ContainerRequestFilter
#Context // request scoped proxy
private ResourceInfo resourceInfo;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
if (resourceInfo.getResourceClass().isAnnotationPresent(MyAnnotationion.class) ||
resourceInfo.getResourceMethod().isAnnotationPresent(MyOtherAnnotation.class)) {
to register the filter use
bind(AuthFilter.class).to(ContainerRequestFilter.class).in(Singleton.class);

Spring MVC like processing of AMQP messages

What I want to do is process AMQP messages in a very similar way the Http Requests are processed using spring-webmvc annotations such as #RequestMapping, #RequestParam etc. But, instead of the Http Request my source object will be an AMQP message. The AMQP message request will have two headers, for example -
method="POST"
url="/api/myobjects/{someParam}"
and the payload will contain data in json format.
If you have noticed, this is nothing but HTTP REST api mapped to AMQP message.
I want to be able to write a controller like handler, for example -
#Controller
public class MyObjectHandler {
#RequestMapping(value="/api/myobjects/{someParam}", method="POST")
public MyObject createMyObject(#Payload MyObject myObj, #PathParam String someParam) {
//... some processing
return myObj;
}
// ...more handlers
}
I have looked at spring-amqp/rabbitmq annotations and also spring integration annotations. They are close to what I want, but would not allow routing to handler methods based on header parameters, especially the REST url.
I don't expect that a readymade solution would be available for this. Just want to make sure I choose the best possible option. Some of the options I think are (in order of precedence)
If the spring-webmvc annotation processing mechanism is extensible, just extend it to use AMQP message as source instead of Http Request
Modify the spring-webmvc annotation processing mechanism to take the AMQP message as input instead of Http Request
Write your own solution with custom annotaions and their processors, which I think is a very involving task
Or any other possible approach than above?
Any guidance/direction is appreciated.
I think the starting point is likely AbstractMethodMessageHandler in spring-messaging.
There's currently a SimpAnnotationMethodMessageHandler implementation for websockets which invokes #Controllers.
You could use a #RabbisListener method that has a Message<?> parameter (Spring AMQP will convert the underlying Rabbit message to a spring-messaging message, including the headers). Then, invoke the message handler to route to the appropriate controller method.
If you come up with a robust implementation, please consider contributing it.

Categories