Spring async controller method with DeferredResult and logback-access for full request/response logging - spring

Spring async controller method with DeferredResult and logback-access for full request/response logging. The problem is that for synchronous methods for example that return simple String request body is written into the log file but for methods that return Asynchronously logback-access fails to print response body. BTW, in last case headers get printed.
Logback-access is installed on container(Tomcat 8) level.
Let me know if I can provide more details

Related

Add sleuth trace id to request header

I have a spring boot application having dependency of spring-cloud-starter-sleuth-3.0.3 and spring-cloud-sleuth-zipkin-3.0.3.
I have a requirement that i need to pass trace-id to request header while calling API from webclient.
Demo webClient
#Slf4j
#Component
#RequiredArgsConstructor
public class DemoApiClient {
private final WebClient demoWebClient;
private final DemoProperties demoProperties;
private final Tracer tracer;
public Mono<DemoDetail> retrieveDemoDetail(String demo){
return demoWebClient
.get()
.uri(uriBuilder->uriBuilder
.path(demoProperties.getLookupPath())
.build(demo))
.header("trace-id", tracer.currentSpan().context().traceId())
.accept(APPLICATION_JSON)
.retrieve()
.bodyToMono(DemoDetail.class)
.doOnError(e -> log.error("Could not find demo", e));
}
}
tracer.currentSpan() is coming as null , hence NPE is thrown.
As per document, approach is given to add trace-id to header of response
https://docs.spring.io/spring-cloud-sleuth/docs/3.0.3/reference/html/howto.html#how-to-add-headers-to-the-http-server-response.
However, i need correct approach to add trace-id to request header.
WebClient is instrumented, please see the docs: WebClient integration, so tracing information should be propagated out of the box over the wire.
If you want to do this manually (I don't recommend), you need to check what you do "above" this method that prevents the tracing information to be propagated. E.g.: you are switching threads, coming from an imperative context to Reactor, etc. You can work this around by getting the tracing information before the switch and either propagate it (see Scope) or inject it into this method.
Also, you are not sending the whole trace context just the traceId so please check the docs and let Sleuth propagate the tracing information for you.
If you are creating a WebClient bean, you are not switching threads, or going back and forth between imperative and reactive, and you still don't see the tracing information in your header (propagated by Sleuth), you can try modifying the instrumentation mechanism, I recommend using DECORATE_QUEUES.
Also, Sleuth 3.1.x is out, you can try upgrading it.

Spring sleuth and spring integration generating the same traceId for all outbound API requests

I'm building a springboot app which use spring integration and spring sleuth as well. The app reads from a csv file and for each record in the csv file a call is made to an API using spring resttemplate. Each time a file is read, all corresponding calls to the API are having the same X-B3-TraceId. They do have a different spanId.
I would like to have different X-B3-TraceId for each call to the API. I believe the spring integration is setting a traceId for each file read operation and using the same throughout during each call to the API.
#Bean
public IntegrationFlow bridgeFlow() {
return IntegrationFlows.from(ABC_SERVICE_QUEUE_CHANNEL)
.bridge(e -> e.poller(Pollers.fixedDelay(period).maxMessagesPerPoll(MAX_MSG_PER_POLL)))
.handle(someService, "someMethod")
.route(router())
.get();
}
"someMethod" has the call to the API using resttemplate as,
ResponseEntity<String> response = restTemplate.exchange(someUrl, HttpMethod.POST, requestEntity, String.class);
I tried manually setting the X-B3-TraceId headers but that seem to be getting overridden
If you'd like to override some header with your own value, you should use something like this in your IntegrationFlow:
.enrichHeaders(s -> s.header("X-B3-TraceId", "some_value", true))
The last argument is important here to be set to true.
See its JavaDocs:
#param overwrite true to overwrite an existing header.
On the other hand it is not clear from your code snippet how that X-B3-TraceId header appears in the message at all. Who sets it for us? And where is your file reader and splitter for records in it?

How to read request in an Interceptor in Spring

I can't use getReader() because in some other class getReader() is already used and the logic can't be changed.
How can I read it from interceptor?
I need to persist request, response and status from interceptor. So for this, I need to access request and response.
Trying to call service from preHandle()

Spring Data REST CORS - how to handle preflight OPTIONS request?

I'm using Spring Data REST to build a RESTful API. Until now my HTML GUI for this RESTful service was served from the same Tomcat and I had no problems wit Cross Origin requests.
Now I want to serve the static files from a different server. This means the API is on another domain/port. Browsers will send the OPTIONS request to get the Access-Control headers from the server. Unfortunately Spring Data REST does not handle those OPTIONS requests and even returns a HTTP 500.
I tried creating a custom controller that handles all OPTIONS requests
#Controller
#RequestMapping(value = "/**", method = RequestMethod.OPTIONS)
public class OptionsController {
#RequestMapping
public ResponseEntity options() {
return new ResponseEntity<Void>(HttpStatus.OK);
}
}
Which worked for OPTIONS, but then all other requests (like GET) ceased to work.
OPTIONS requests are switched on via the dispatchOptionsRequest dispatcher servlet parameter.
tl;dr: currently Spring Data REST does not answer OPTIONS requests at all.
It might be worth opening a ticket in our JIRA.
Browsers will send the OPTIONS request to get the Access-Control headers from the server.
Is that specified somewhere? If so, it would be cool if the ticket description included a link to that spec.
A few comments regarding your approach for a workaround:
#RequestMapping on the controller method overrides the method attribute and expectedly now matches all HTTP methods, which is why you see all requests intercepted. So you need to define OPTIONS as HTTP method there, too (or maybe instead of in the class mapping).
You're not returning any Allow header which is the whole purpose of OPTIONS in the first place.
I wonder if the approach in general makes sense as it'll be hard to reason about the supported HTTP methods in general.
Just set the parameter dispatchOptionsRequest to true into the dispatcher to process the Options method calls, into the implementation of the WebApplicationInitializer.
ServletRegistration.Dynamic dispatcher = container.addServlet("dispatcher", new DispatcherServlet(applicationContext));
dispatcher.setInitParameter("dispatchOptionsRequest", "true");
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/*");

spring deferredresult

I am new to spring and want to implement long polling for a website to display admin message immediately when it becomes available to all clients,i searched google for hours and could only find out deferredresult(spring 3.2) can be used to implement it.my question is how i can achieve long polling with deferredresult, I would appreciate it if anyone could refer me to such a tutorial.
Another option is to use AsyncContext. This will keep the initial GET request "open" and enable you to send multiple messages as part of the response, unlike DeferredResult which allows to send only ONE response message. Here is a good-link that explains how !
Straight from the horses mouth.
You have two basic options: Option 1 is a Callable
, where the Callable returns the String view name (you may also be able to use #ResponseBody or some of the other normal Spring return types like ModelAndView, but I have never investigated that).
Option two is to return DeferredResult, which is like Callable. except you can pass that off to a separate thread and fill in the results there. Again, not sure if you can return a ModelAndView or use #ResponseBody to return XML/JSON, but I am sure you can.
Short background about DeferredResult:
Your controller is eventually a function executed by the servlet container (for that matter, let's assume that the server container is Tomcat) worker thread. Your service flow start with Tomcat and ends with Tomcat. Tomcat gets the request from the client, holds the connection, and eventually returns a response to the client. Your code (controller or servlet) is somewhere in the middle.
Consider this flow:
Tomcat get client request.
Tomcat executes your controller.
Release Tomcat thread but keep the client connection (don't return response) and run heavy processing on different thread.
When your heavy processing complete, update Tomcat with its response and return it to the client (by Tomcat).
Because the servlet (your code) and the servlet container (Tomcat) are different entities, then to allow this flow (releasing tomcat thread but keep the client connection) we need to have this support in their contract, the package javax.servlet, which introduced in Servlet 3.0 . Spring MVC use this new Servlet 3.0 capability when the return value of the controller is DeferredResult or Callable, although they are two different things. Callable is an interface that is part of java.util, and it is an improvement for the Runnable interface. DeferredResult is a class designed by Spring to allow more options (that I will describe) for asynchronous request processing in Spring MVC, and this class just holds the result (as implied by its name) while your Callable implementation holds the async code. So it means you can use both in your controller, run your async code with Callable and set the result in DeferredResult, which will be the controller return value. So what do you get by using DeferredResult as the return value instead of Callable? DeferredResult has built-in callbacks like onError, onTimeout, and onCompletion. It makes error handling very easy. In addition, as it is just the result container, you can choose any thread (or thread pool) to run on your async code. With Callable, you don't have this choice.
Here you can find a simple working examples I created with both options, Callable and DeferredResult.

Resources