/** * Helper method for creating a `CompletableFuture` that is using the tracing helpers. * <p> * <pre> * AsyncNettyHelper.supplyAsync(() -> { * //do some work in a background thread * return VOID; * }, executor, ctx); * </pre> */ public static <U> CompletableFuture<U> supplyAsync(Supplier<U> f, Executor executor, ChannelHandlerContext ctx) { return CompletableFuture.supplyAsync(supplierWithTracingAndMdc(f, ctx), executor); }
/** * Helper method for creating a `CompletableFuture` that is using the tracing helpers. * <p> * <pre> * AsyncNettyHelper.supplyAsync(() -> { * //do some work in a background thread * return VOID; * }, executor, ctx); * </pre> */ public static <U> CompletableFuture<U> supplyAsync(Supplier<U> f, Executor executor, ChannelHandlerContext ctx) { return CompletableFuture.supplyAsync(supplierWithTracingAndMdc(f, ctx), executor); }
protected String extractDistributedTraceId(RequestInfo requestInfo, ChannelHandlerContext ctx) { String traceId = (requestInfo == null) ? null : requestInfo.getHeaders().get(TraceHeaders.TRACE_ID); if (traceId == null) { traceId = supplierWithTracingAndMdc( () -> { Span currentSpanFromTracer = Tracer.getInstance().getCurrentSpan(); if (currentSpanFromTracer != null) return currentSpanFromTracer.getTraceId(); return null; }, ctx ).get(); } return traceId; }
protected String extractDistributedTraceId(RequestInfo requestInfo, ChannelHandlerContext ctx) { String traceId = (requestInfo == null) ? null : requestInfo.getHeaders().get(TraceHeaders.TRACE_ID); if (traceId == null) { traceId = supplierWithTracingAndMdc( () -> { Span currentSpanFromTracer = Tracer.getInstance().getCurrentSpan(); if (currentSpanFromTracer != null) return currentSpanFromTracer.getTraceId(); return null; }, ctx ).get(); } return traceId; }
/** * Sample service endpoint using CompletableFuture.supplyAsync to illustrate how to create an asynchronous * call. Where the service task will be completed without making external calls or compute-intensive code * it would be better to call: * * <pre> * return CompletableFuture.completedFuture(ResponseInfo.newBuilder("Hello, world!") * .withDesiredContentWriterMimeType("text/plain") * .build(); * </pre> * * Also note that because we use {@link AsyncNettyHelper#supplierWithTracingAndMdc(Supplier, ChannelHandlerContext)} * to wrap the endpoint logic, the log message will be automatically tagged with the trace ID associated with the * incoming request. */ @Override public CompletableFuture<ResponseInfo<String>> execute(RequestInfo<Void> request, Executor longRunningTaskExecutor, ChannelHandlerContext ctx) { return CompletableFuture.supplyAsync(supplierWithTracingAndMdc( () -> { logger.debug("Processing Request..."); return ResponseInfo.newBuilder("Hello, world!").withDesiredContentWriterMimeType("text/plain").build(); }, ctx) ); }
@Override public CompletableFuture<ResponseInfo<String>> execute(RequestInfo<Void> request, Executor longRunningTaskExecutor, ChannelHandlerContext ctx) { //need to do some work in a future to force the DecoderException to bubble up and return a 400 return CompletableFuture.supplyAsync(supplierWithTracingAndMdc(() -> { try { sleep(10); } catch (InterruptedException e) { } return null; }, ctx), longRunningTaskExecutor).thenApply(o -> ResponseInfo.newBuilder(RESPONSE_PAYLOAD).build()); }
@Override public CompletableFuture<ResponseInfo<String>> execute(RequestInfo<Void> request, Executor longRunningTaskExecutor, ChannelHandlerContext ctx) { //need to do some work in a future to force the DecoderException to bubble up and return a 400 return CompletableFuture.supplyAsync(supplierWithTracingAndMdc(() -> { try { sleep(10); } catch (InterruptedException e) { } return null; }, ctx), longRunningTaskExecutor).thenApply(o -> ResponseInfo.newBuilder(RESPONSE_PAYLOAD).build()); }
/** * Helper method for creating a `CompletableFuture` that has the `Supplier` wrapped around a SubSpan. * <p> * You would prefer this method over the above when your `Supplier` has logic that makes an outbound/downstream call * and you do not want the use of a `CircuitBreaker`. * <p> * You would prefer this method over {@link #supplyAsync(String, Supplier, CircuitBreaker, Executor, ChannelHandlerContext)} * when your `Supplier` has logic that you would like wrapped with distributed tracing logs and not use a {@link CircuitBreaker} * <p> * An example would be using a client SDK that makes blocking HTTP calls. * <p> * The SubSpan purpose will be set to `CLIENT` as this is the typical use case when utilizing these helpers. * <p> * <pre> * AsyncNettyHelper.supplyAsync("someWorkToBeDone", () -> { * //do some work in a background thread * return VOID; * }, executor, ctx); * </pre> */ public static <U> CompletableFuture<U> supplyAsync(String subSpanName, Supplier<U> f, Executor executor, ChannelHandlerContext ctx) { return CompletableFuture.supplyAsync(supplierWithTracingAndMdc(() -> { try { Tracer.getInstance().startSubSpan(subSpanName, Span.SpanPurpose.CLIENT); return f.get(); } finally { Tracer.getInstance().completeSubSpan(); } }, ctx), executor); }
/** * Helper method for creating a `CompletableFuture` that has the `Supplier` wrapped around a SubSpan. * <p> * You would prefer this method over the above when your `Supplier` has logic that makes an outbound/downstream call * and you do not want the use of a `CircuitBreaker`. * <p> * You would prefer this method over {@link #supplyAsync(String, Supplier, CircuitBreaker, Executor, ChannelHandlerContext)} * when your `Supplier` has logic that you would like wrapped with distributed tracing logs and not use a {@link CircuitBreaker} * <p> * An example would be using a client SDK that makes blocking HTTP calls. * <p> * The SubSpan purpose will be set to `CLIENT` as this is the typical use case when utilizing these helpers. * <p> * <pre> * AsyncNettyHelper.supplyAsync("someWorkToBeDone", () -> { * //do some work in a background thread * return VOID; * }, executor, ctx); * </pre> */ public static <U> CompletableFuture<U> supplyAsync(String subSpanName, Supplier<U> f, Executor executor, ChannelHandlerContext ctx) { return CompletableFuture.supplyAsync(supplierWithTracingAndMdc(() -> { try { Tracer.getInstance().startSubSpan(subSpanName, Span.SpanPurpose.CLIENT); return f.get(); } finally { Tracer.getInstance().completeSubSpan(); } }, ctx), executor); }
@Test public void supplierWithTracingAndMdc_ctx_works_as_expected() { // given Pair<Deque<Span>, Map<String, String>> setupInfo = setupStateWithTracingAndMdcInfo(); // when Supplier result = AsyncNettyHelper.supplierWithTracingAndMdc(supplierMock, ctxMock); // then verifySupplierWithTracingAndMdcSupport(result, supplierMock, setupInfo.getLeft(), setupInfo.getRight()); }
@Test public void supplierWithTracingAndMdc_pair_works_as_expected() { // given Pair<Deque<Span>, Map<String, String>> setupInfo = generateTracingAndMdcInfo(); // when Supplier result = AsyncNettyHelper.supplierWithTracingAndMdc(supplierMock, setupInfo); // then verifySupplierWithTracingAndMdcSupport(result, supplierMock, setupInfo.getLeft(), setupInfo.getRight()); }
@Test public void supplierWithTracingAndMdc_separate_args_works_as_expected() { // given Pair<Deque<Span>, Map<String, String>> setupInfo = generateTracingAndMdcInfo(); // when Supplier result = AsyncNettyHelper.supplierWithTracingAndMdc(supplierMock, setupInfo.getLeft(), setupInfo.getRight()); // then verifySupplierWithTracingAndMdcSupport(result, supplierMock, setupInfo.getLeft(), setupInfo.getRight()); }