@Override public O serve(ServiceRequestContext ctx, I req) throws Exception { final TraceData traceData = getTraceData(ctx, req); final String method = req instanceof RpcRequest ? ((RpcRequest) req).method() : ctx.method(); final ServerRequestAdapter requestAdapter = new InternalServerRequestAdapter(method, traceData); final ServerSpan serverSpan = serverInterceptor.openSpan(requestAdapter); final boolean sampled; if (serverSpan != null) { ctx.onEnter(() -> serverInterceptor.setSpan(serverSpan)); ctx.onExit(serverInterceptor::clearSpan); sampled = serverSpan.getSample(); } else { sampled = false; } try { final O res = delegate().serve(ctx, req); if (sampled) { ctx.requestLogFuture() .thenAcceptBoth( res.closeFuture(), (log, unused) -> closeSpan(ctx, serverSpan, log, res)) .exceptionally(CompletionActions::log); } return res; } finally { serverInterceptor.clearSpan(); } }
/** Returns the server-side annotations that should be added to a Zipkin span. */ protected List<KeyValueAnnotation> annotations(ServiceRequestContext ctx, RequestLog req, O res) { final List<KeyValueAnnotation> annotations = new ArrayList<>(5); final StringBuilder uriBuilder = new StringBuilder(); uriBuilder.append(req.scheme().uriText()); uriBuilder.append("://"); uriBuilder.append(req.host()); uriBuilder.append(ctx.path()); if (req.method() != null) { uriBuilder.append('#'); uriBuilder.append(req.method()); } annotations.add(KeyValueAnnotation.create("server.uri", uriBuilder.toString())); if (ctx.remoteAddress() != null) { annotations.add(KeyValueAnnotation.create("server.remote", ctx.remoteAddress().toString())); } if (ctx.localAddress() != null) { annotations.add(KeyValueAnnotation.create("server.local", ctx.localAddress().toString())); } final CompletableFuture<?> f = res.closeFuture(); if (f.isDone()) { // Need to use a callback because CompletableFuture does not have a getter for the cause of // failure. // The callback will be invoked immediately because the future is done already. f.handle( voidFunction( (result, cause) -> { final String resultText = cause == null ? "success" : "failure"; annotations.add(KeyValueAnnotation.create("server.result", resultText)); if (cause != null) { annotations.add(KeyValueAnnotation.create("server.cause", cause.toString())); } })) .exceptionally(CompletionActions::log); } return annotations; }
private static SpanCollectingReporter testServiceInvocation(boolean sampled) throws Exception { SpanCollectingReporter reporter = new SpanCollectingReporter(); Brave brave = new Brave.Builder(TEST_SERVICE) .reporter(reporter) .traceSampler(Sampler.create(1.0f)) .build(); @SuppressWarnings("unchecked") Service<ThriftCall, ThriftReply> delegate = mock(Service.class); TraceData traceData = TraceData.builder() .sample(sampled) .spanId(SpanId.builder().traceId(1).spanId(2).parentId(3L).build()) .build(); TracingServiceImpl stub = new TracingServiceImpl(delegate, brave, traceData); ThriftCall req = new ThriftCall(0, HelloService.Iface.class, "hello", "trustin"); DefaultRequestLog reqLog = new DefaultRequestLog(); reqLog.start(mock(Channel.class), SessionProtocol.H2C, "localhost", TEST_METHOD, "/"); reqLog.end(); ServiceRequestContext ctx = mock(ServiceRequestContext.class); // AbstractTracingService prefers RpcRequest.method() to ctx.method(), so "POST" should be // ignored. when(ctx.method()).thenReturn("POST"); when(ctx.requestLogFuture()).thenReturn(reqLog); ctx.onEnter(ArgumentMatchers.isA(Runnable.class)); ctx.onExit(ArgumentMatchers.isA(Runnable.class)); ThriftReply res = new ThriftReply(0, "Hello, trustin!"); when(delegate.serve(ctx, req)).thenReturn(res); // do invoke stub.serve(ctx, req); verify(delegate, times(1)).serve(eq(ctx), eq(req)); return reporter; }