Tracking response times of API calls in springboot - spring

I'm looking to track the response times of API calls.
I then want to plot the response times of the calls( GET, PUT, POST DELETE) on a graph afterwards to compare the time differences.
This is what I'm currently doing to find the response time of a GET call but I'm not quite sure if it's right.
#RequestMapping(value="/Students", method = RequestMethod.GET)
public ResponseEntity<List<Students>> getStudents()
{
long beginTime = System.currentTimeMillis();
List<Students> students = (List<Students>) repository.findAll();
if(students.isEmpty())
{
return new ResponseEntity(HttpStatus.NO_CONTENT);
}
long responseTime = System.currentTimeMillis() - beginTime;
logger.info("Response time for the call was "+responseTime);
return new ResponseEntity(students, HttpStatus.OK);
}
I believe I am returning the response time before I actually return the data to the client which is the whole point of this but I wouldn't be able to put it after the return statement as it would be unreachable code.
Are there any better ways of trying to track the times of the calls?

You can use Around Advice of springboot and in the advice you can log the time. The way it works is once a call is made to the controller, the Around Advice intercepts it and starts a Timer(to record the time taken). From the advice we proceed to the main controller using jointPoint.proceed() method. Once the controller returns the value you can log the timer value. return the Object.
Here is the sample code:
in build.grale include
compile("org.aspectj:aspectjweaver:1.8.8")
Create a Component Class and put around #Aspect
#Component
#Aspect
public class advice{
#Around(("#annotation(logger)")
public Object loggerAspect(ProceedingJoinPoint joinPoint){
// start the Timer
Object object = jointPoint.proceed();
// log the timer value
return object;
}
}
Add annotation #logger in the controller method. you are good to go.
Hope this helps.
you can refer the link for full explanation.

Related

Mutiny Uni Convert to Primitive Type

Up until now I have done very basic things with smallrye Mutiny in Quarkus. Basically, I have one or two very small web services which only interact with a web application. These services return a Uni<Response>.
Now I'm writing a logging service I want my others to pass information to. In this logging service, I need to return a value to calling services. The logging service will return this value as a Uni<Integer>. What I'm struggling with is how to extract the return value in the calling service as an int.
Here is the function in the logging service
#GET
#Path("/requestid")
#Produces(MediaType.TEXT_PLAIN)
public Uni<Integer> getMaxRequestId(){
return service.getMaxRequestId();
}
public Uni<Integer> getMaxRequestId() {
Integer result = Integer.valueOf(em.createQuery("select MAX(request_id) from service_requests").getFirstResult());
if(result == null) {
result = 0;
}
return Uni.createFrom().item(result += 1);
}
And here is the client side code in the calling service
#Path("/requests")
public class RequestIdResource {
#RestClient
RequestIdServices service;
#GET
#Path("/requestid")
#Produces(MediaType.TEXT_PLAIN)
public Uni<Integer> getMaxRequestId(){
return service.getMaxRequestId();
}
}
public void filter(ContainerRequestContext requestContext) throws IOException {
int requestid = client.getMaxRequestId();
rm.name = ConfigProvider.getConfig().getValue("quarkus.application.name", String.class);
rm.server = requestContext.getUriInfo().getBaseUri().getHost();
rm.text = requestContext.getUriInfo().getPath(true);
rm.requestid = requestid;
}
Basically everything I have tried creates another Uni. Maybe I am simply using the concept all wrong. But how do I get the Integer out of the Uni so I can get the intValue?
You need to invoke a terminal operation, or use the value and continue the chain.
If you want to invoke a terminal operator you can invoke the await operation to make your code blocking and wait for the response.
If you want to merge this reactive invocation with another that is present in your client code, you can join or combine your actual Mutiny stream with the on coming from the response by using the combine method.
If you just want to use the value and do not retrieve it, you can suscribe and get the result.
If you have a multi you can call directly the method toList
Assuming that you want to have some timeouts involved and you want to get the actual Integer, you can go with the await method and a timeout.

Keep calling 3rd party until it returns expected response with Hystrix

I am looking for a way to call 3rd party service from my code(Spring Boot app), and in case it is unresponsive, I would like to repeat the call x amount of times and then provide a default fallback. I found an example pseudocode that would probably work in my case with Hystrix
public class ExampleClass {
#HystrixCommand(fallbackMethod = "example_Fallback")
public String myMethod() {
// third party service
String response = httpClient.execute();
return "OK";
}
private String example_Fallback() {
return "ERROR HAPPENED";
}
}
However, I would also like to repeat the call to same third-party service x amount of times if it returns a normal response that's unexpected.(treat that specific response as if the third party is unresponsive). The reason for that is because, third party might not be able to serve the request and I can only check that in the response. Could someone point me in the right direction or provide an example how this could be solved with Hystrix ?
...I would like to repeat the call x amount of times and then provide
a default fallback.
Configuring circuitBreaker.requestVolumeThreshold may help here. Take a look at the other Hystrix properties as well.
#HystrixCommand(fallbackMethod = "example_Fallback", commandProperties = {
#HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "5"),
#HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "2000")
}
)
public String myMethod() {
...
}
Notice that circuitBreaker.requestVolumeThreshold (quote) "...sets the minimum number of requests in a rolling window that will trip the circuit". The rolling window duration - metrics.rollingStats.timeInMilliseconds - defaults to 10 seconds.
There's also the #Retryable annotation in Spring.

Prometheus + Micrometer: how to record time intervals and success/failure rates

I am sending from a front-end client to a metrics-microservice a JSON with the following data:
{
totalTimeOnTheNetwork: number;
timeElasticsearch: number;
isSuccessful: boolean;
}
The metrics-microservice currently handles the data like this:
#AllArgsConstructor
#Service
public class ClientMetricsService {
#Autowired
MeterRegistry registry; // abstract class, SimpleMeterRegistry gets injected
public void metrics(final MetricsProperty metrics) {
final long networkTime = metrics.getTotalTime() - metrics.getElasticTime();
registry.timer(ELASTIC_TIME_LABEL).record(metrics.getElasticTime(), TimeUnit.MILLISECONDS);
registry.timer(TOTAL_TIME_LABEL).record(metrics.getTotalTime(), TimeUnit.MILLISECONDS);
registry.timer(NETWORK_TIME_LABEL).record(networkTime, TimeUnit.MILLISECONDS);
}
}
As you can see I make a new metric for each of the time intervals. I was wondering if I can put all the intervals into one metric? It would be great if I did not have to calculate network-time on the metrics-microservice but rather in Grafana.
Also, could I put a success/failure tag inside the registry.timer? I assume I need to use a timer.builder on every request then like this:
Timer timer = Timer
.builder("my.timer")
.description("a description of what this timer does") // optional
.tags("region", "test") // optional
.register(registry);
Is that a typical way to do it (eg create a new timer on every HTTP request and link it to the registry) or should the timer be derived from the MeterRegistry like in my current version?
Or would you use another metric for logging success/failure? In the future instead of a boolean, the metric might change to a http-error-code for example, so I am not sure how to implement it in a maintainable way
Timer timer = Timer
.builder("your-timer-name-here")
.tags("ResponseStatus", isSuccessful.toString, "ResponseCode", http-error-code.toString)
.register(registry);
timer.record(metrics.getTotalTime);
Should be working code that responds to your question but I have a feeling there is a misunderstanding. Why do you want everything in one metric?
Either way you can probably sort that out with tags. I do not know the capabilities on the Grafana end but it might be as simple as throwing the .getElasticTime info into another tag and sending it through.

Service method transactionality when not using exceptions as flow control in Spring Boot

I have the following method in an #Service class which has #Transactional defined:
#Override
public Result add(#NonNull final UserSaveRequest request) {
final Result<Email> emailResult = Email.create(request.getEmail());
final Result<UserFirstName> userFirstNameResult = UserFirstName.create(request.getFirstName());
final Result<UserLastName> userLastNameResult = UserLastName.create(request.getLastName());
final Result combinedResult = Result.combine(emailResult, userFirstNameResult, userLastNameResult);
if (combinedResult.isFailure()) {
return Result.fail(combinedResult.getErrorMessage());
}
final Result<User> userResult = User.create(emailResult.getValue(), userFirstNameResult.getValue(), userLastNameResult.getValue());
if (userResult.isFailure()) {
return Result.fail(userResult.getErrorMessage());
}
this.userRepository.save(userResult.getValue());
return Result.ok();
}
Now as you can see I utilize a Result class which can contain a return value or an error message as I don't think using exceptions for flow control is very clean.
The problem I now have is; the complete method is bound in one transaction and if one database call should fail the whole transaction will be rolled back. In my model however, after the this.userRepository.save(userResult.getValue()); call, if something would happen that would force me to return a failed result, I can't undo that save(userResult.getVlaue()); call seeing as I don't use exceptions for flow control.
Is this a problem that has an elegant solution, or is this a place where I need to make a trade-off between using exceptions as flow control and having to mentally keep track of the ordering of my statements in these kind of situations?
Yes, you can trigger rollback manually. Try this:
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
More information: https://docs.spring.io/spring/docs/5.0.7.RELEASE/spring-framework-reference/data-access.html#transaction-declarative-rolling-back

Spring Sleuth - Tracing Failures

In a microservice environment I see two main benefits from tracing requests through all microservice instances over an entire business process.
Finding latency gaps between or in service instances
Finding roots of failures, whether technical or regarding the business case
With Zipkin there is a tool, which addresses the first issue. But how can tracing be used to unveil failures in your microservice landscape? I definitely want to trace all error afflicted spans, but not each request, where nothing went wrong.
As mentioned here a custom Sampler could be used.
Alternatively, you may register your own Sampler bean definition and programmatically make the decision which requests should be sampled. You can make more intelligent choices about which things to trace, for example, by ignoring successful requests, perhaps checking whether some component is in an error state, or really anything else.
So I tried to implement that, but it doesn't work or I used it wrong.
So, as the blog post suggested I registered my own Sampler:
#Bean
Sampler customSampler() {
return new Sampler() {
#Override
public boolean isSampled(Span span) {
boolean isErrorSpan = false;
for(String tagKey : span.tags().keySet()){
if(tagKey.startsWith("error_")){
isErrorSpan = true;
}
}
return isErrorSpan ;
}
};
}
And in my controller I create a new Span, which is being tagged as an error if an exception raises
private final Tracer tracer;
#Autowired
public DemoController(Tracer tracer) {
this.tracer = tracer;
}
#RequestMapping(value = "/calc/{i}")
public String calc(#PathVariable String i){
Span span = null;
try {
span = this.tracer.createSpan("my_business_logic");
return "1 / " + i + " = " + new Float(1.0 / Integer.parseInt(i)).toString();
}catch(Exception ex){
log.error(ex.getMessage(), ex);
span.logEvent("ERROR: " + ex.getMessage());
this.tracer.addTag("error_" + ex.hashCode(), ex.getMessage());
throw ex;
}
finally{
this.tracer.close(span);
}
}
Now, this doesn't work. If I request /calc/a the method Sampler.isSampled(Span) is being called before the Controller method throws a NumberFormatException. This means, when isSampled() checks the Span, it has no tags yet. And the Sampler method is not being called again later in the process. Only if I open the Sampler and allow every span to be sampled, I see my tagged error-span later on in Zipkin. In this case Sampler.isSampled(Span) was called only 1 time but HttpZipkinSpanReporter.report(Span) was executed 3 times.
So what would the use case look like, to transmit only traces, which have error spans ? Is this even a correct way to tag a span with an arbitrary "error_" tag ?
The sampling decision is taken for a trace. That means that when the first request comes in and the span is created you have to take a decision. You don't have any tags / baggage at that point so you must not depend on the contents of tags to take this decision. That's a wrong approach.
You are taking a very custom approach. If you want to go that way (which is not recommended) you can create a custom implementation of a SpanReporter - https://github.com/spring-cloud/spring-cloud-sleuth/blob/master/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/SpanReporter.java#L30 . SpanReporter is the one that is sending spans to zipkin. You can create an implementation that will wrap an existing SpanReporter implementation and will delegate the execution to it only when some values of tags match. But from my perspective it doesn't sound right.

Resources