open-telemetryopen-telemetry-java

opentelemetry missing trace


I am trying the opentelemetry auto-instrumentation. But it appears that the trace is being lost when using CompletableFuture.handle. Could anyone please assist me with this issue?

@GetMapping("/future3")
public CompletableFuture<ResponseEntity<String>> future3() {
    LOGGER.info("future1 ");  // <- we have trace  here
    LOGGER.info("trace={}", Span.current().getSpanContext().getTraceId()); // <- we have trace  here

    SqsAsyncClient sqsAsyncClient = SqsAsyncClient.builder().build(); 
    CompletableFuture<ResponseEntity<String>> aa = sqsAsyncClient
        .sendMessage(build -> build.queueUrl(queueUrl).messageBody("hello"))
        .handle((response, error) -> {
            LOGGER.info("trace response={}", Span.current().getSpanContext().getTraceId()); // <- we lost trace  

            LOGGER.info("test response={}", response);   // <- we lost trace  
            return new ResponseEntity<>("success", HttpStatus.OK);

            });
    LOGGER.info("future1_1"); // <- we have trace id 
    return aa;
}

Solution

  • I did have the same issue and luckily i can provide you the solution from your code I see that the issue you're have with losing the trace context when using CompletableFuture.handle. and as a solution I suggest that you can use Context, or maybe I see Span a good choice to propagation feature of OpenTelemetry explicitly to ensure that the trace context is propagated into the asynchronous callback. So first of all, Context can capture and pass explicitly to asynchronous tasks, and for me I suggest to Capture the current Context at the beginning of the method, and then use this captured context inside your .handle method

    Well I did as well make some modification the code to help you to have fast product and this is the code:

    import io.opentelemetry.context.Context;
    import io.opentelemetry.context.Scope;
    
    @GetMapping("/future3")
    public CompletableFuture<ResponseEntity<String>> future3() {
    LOGGER.info("future1 ");
    LOGGER.info("trace={}", Span.current().getSpanContext().getTraceId());
    
    SqsAsyncClient sqsAsyncClient = SqsAsyncClient.builder().build();
    

    // Capture the current OpenTelemetry context

    Context currentContext = Context.current();
    
    CompletableFuture<ResponseEntity<String>> aa = sqsAsyncClient
            .sendMessage(build -> build.queueUrl(queueUrl).messageBody("hello"))
            .handle((response, error) -> {
    

    // Make the OpenTelemetry context current within this handle block

                try (Scope scope = currentContext.makeCurrent()) {
                    LOGGER.info("trace response={}", Span.current().getSpanContext().getTraceId());
                    LOGGER.info("test response={}", response);
                    return new ResponseEntity<>("success", HttpStatus.OK);
                }
            });
    
    LOGGER.info("future1_1");
    return aa;
    }
    

    So this all I have to help I hope it works. Good Luck 😉🙏