mirror of https://github.com/dapr/java-sdk.git
				
				
				
			Refactor tracing demo to show A->B->C call stack. (#436)
This commit is contained in:
		
							parent
							
								
									6a44d51279
								
							
						
					
					
						commit
						9e13586ecc
					
				|  | @ -31,7 +31,7 @@ public class InvokeClient { | |||
|   /** | ||||
|    * Identifier in Dapr for the service this client will invoke. | ||||
|    */ | ||||
|   private static final String SERVICE_APP_ID = "tracingdemo"; | ||||
|   private static final String SERVICE_APP_ID = "tracingdemoproxy"; | ||||
| 
 | ||||
|   /** | ||||
|    * Starts the invoke client. | ||||
|  | @ -45,7 +45,7 @@ public class InvokeClient { | |||
|     try (DaprClient client = (new DaprClientBuilder()).build()) { | ||||
|       for (String message : args) { | ||||
|         try (Scope scope = span.makeCurrent()) { | ||||
|           InvokeServiceRequestBuilder builder = new InvokeServiceRequestBuilder(SERVICE_APP_ID, "echo"); | ||||
|           InvokeServiceRequestBuilder builder = new InvokeServiceRequestBuilder(SERVICE_APP_ID, "proxy_echo"); | ||||
|           InvokeServiceRequest request | ||||
|               = builder.withBody(message).withHttpExtension(HttpExtension.POST).withContext(Context.current()).build(); | ||||
|           client.invokeMethod(request, TypeRef.get(byte[].class)) | ||||
|  | @ -54,7 +54,7 @@ public class InvokeClient { | |||
|                 return r; | ||||
|               }) | ||||
|               .flatMap(r -> { | ||||
|                 InvokeServiceRequest sleepRequest = new InvokeServiceRequestBuilder(SERVICE_APP_ID, "sleep") | ||||
|                 InvokeServiceRequest sleepRequest = new InvokeServiceRequestBuilder(SERVICE_APP_ID, "proxy_sleep") | ||||
|                     .withHttpExtension(HttpExtension.POST) | ||||
|                     .withContext(r.getContext()).build(); | ||||
|                 return client.invokeMethod(sleepRequest, TypeRef.get(Void.class)); | ||||
|  |  | |||
|  | @ -3,7 +3,9 @@ | |||
| In this sample, we'll create two java applications: a service application, which exposes two methods, and a client application which will invoke the methods from the service using Dapr. | ||||
| This sample includes: | ||||
| 
 | ||||
| * TracingDemoService (Exposes the method to be remotely accessed) | ||||
| * TracingDemoService (Exposes the methods to be remotely accessed) | ||||
| * TracingDemoServiceController (Implements two methods: `echo` and `sleep`) | ||||
| * TracingDemoMiddleServiceController (Implements two methods: `proxy_echo` and `proxy_sleep`) | ||||
| * InvokeClient (Invokes the exposed methods from TracingDemoService) | ||||
| 
 | ||||
| Also consider [getting started with observability in Dapr](https://github.com/dapr/quickstarts/tree/master/observability). | ||||
|  | @ -54,7 +56,7 @@ CONTAINER ID        IMAGE                  COMMAND                  CREATED | |||
| 
 | ||||
| If Zipkin is not working, [install the newest version of Dapr Cli and initialize it](https://github.com/dapr/cli#install-dapr-on-your-local-machine-self-hosted). | ||||
| 
 | ||||
| ### Running the Demo service sample | ||||
| ### Running the Demo service app | ||||
| 
 | ||||
| The Demo service application exposes two methods that can be remotely invoked. In this example, the service code has two parts: | ||||
| 
 | ||||
|  | @ -82,11 +84,11 @@ This Rest Controller exposes the `echo` and `sleep` methods. The `echo` method r | |||
| public class TracingDemoServiceController { | ||||
|   ///... | ||||
|   @PostMapping(path = "/echo") | ||||
|   public Mono<String> handleMethod(@RequestBody(required = false) byte[] body, | ||||
|   public Mono<String> handleMethod(@RequestBody(required = false) String body, | ||||
|                                    @RequestHeader Map<String, String> headers) { | ||||
|     return Mono.fromSupplier(() -> { | ||||
|       try { | ||||
|         String message = body == null ? "" : new String(body, StandardCharsets.UTF_8); | ||||
|         String message = body == null ? "" : body; | ||||
| 
 | ||||
|         Calendar utcNow = Calendar.getInstance(TimeZone.getTimeZone("GMT")); | ||||
|         String utcNowAsString = DATE_FORMAT.format(utcNow.getTime()); | ||||
|  | @ -130,15 +132,67 @@ dapr run --app-id tracingdemo --app-port 3000 -- java -jar target/dapr-java-sdk- | |||
| 
 | ||||
| Once running, the TracingDemoService is now ready to be invoked by Dapr. | ||||
| 
 | ||||
| ### Running the Demo middle service app | ||||
| 
 | ||||
| ### Running the InvokeClient sample | ||||
| This service will handle the `proxy_echo` and `proxy_sleep` methods and invoke the corresponding methods in the service above. | ||||
| In the code below, the `opentelemetry-context` attribute is used to propagate the tracing context among service invocations in multiple layers. | ||||
| 
 | ||||
| This sample code uses the Dapr SDK for invoking two remote methods (`echo` and `sleep`). It is also instrumented with OpenTelemetry. See the code snippet below: | ||||
| ```java | ||||
| @RestController | ||||
| public class TracingDemoMiddleServiceController { | ||||
|   // ... | ||||
|   @PostMapping(path = "/proxy_echo") | ||||
|   public Mono<byte[]> echo( | ||||
|       @RequestAttribute(name = "opentelemetry-context") Context context, | ||||
|       @RequestBody(required = false) String body) { | ||||
|     InvokeServiceRequestBuilder builder = new InvokeServiceRequestBuilder(INVOKE_APP_ID, "echo"); | ||||
|     InvokeServiceRequest request | ||||
|         = builder.withBody(body).withHttpExtension(HttpExtension.POST).withContext(context).build(); | ||||
|     return client.invokeMethod(request, TypeRef.get(byte[].class)).map(r -> r.getObject()); | ||||
|   } | ||||
|   // ... | ||||
|   @PostMapping(path = "/proxy_sleep") | ||||
|   public Mono<Void> sleep(@RequestAttribute(name = "opentelemetry-context") Context context) { | ||||
|     InvokeServiceRequestBuilder builder = new InvokeServiceRequestBuilder(INVOKE_APP_ID, "sleep"); | ||||
|     InvokeServiceRequest request = builder.withHttpExtension(HttpExtension.POST).withContext(context).build(); | ||||
|     return client.invokeMethod(request, TypeRef.get(byte[].class)).then(); | ||||
|   } | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| The request attribute `opentelemetry-context` in created by parsing the tracing headers in the [OpenTelemetryInterceptor](../../springboot/OpenTelemetryInterceptor.java) class. See the code below: | ||||
| 
 | ||||
| ```java | ||||
| @Component | ||||
| public class OpenTelemetryInterceptor implements HandlerInterceptor { | ||||
|   // ... | ||||
|   @Override | ||||
|   public boolean preHandle( | ||||
|       HttpServletRequest request, HttpServletResponse response, Object handler) { | ||||
|     final TextMapPropagator textFormat = OpenTelemetry.getGlobalPropagators().getTextMapPropagator(); | ||||
|     // ... | ||||
|     Context context = textFormat.extract(Context.current(), request, HTTP_SERVLET_REQUEST_GETTER); | ||||
|     request.setAttribute("opentelemetry-context", context); | ||||
|     return true; | ||||
|   } | ||||
|   // ... | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| Use the follow command to execute the service: | ||||
| 
 | ||||
| ```sh | ||||
| dapr run --app-id tracingdemoproxy --app-port 3001   -- java -jar target/dapr-java-sdk-examples-exec.jar io.dapr.examples.tracing.TracingDemoService -p 3001 | ||||
| ``` | ||||
| 
 | ||||
| ### Running the InvokeClient app | ||||
| 
 | ||||
| This sample code uses the Dapr SDK for invoking two remote methods (`proxy_echo` and `proxy_sleep`). It is also instrumented with OpenTelemetry. See the code snippet below: | ||||
| 
 | ||||
| ```java | ||||
| public class InvokeClient { | ||||
| 
 | ||||
| private static final String SERVICE_APP_ID = "invokedemo"; | ||||
| private static final String SERVICE_APP_ID = "tracingdemoproxy"; | ||||
| ///... | ||||
|   public static void main(String[] args) throws Exception { | ||||
|     Tracer tracer = OpenTelemetryConfig.createTracer(InvokeClient.class.getCanonicalName()); | ||||
|  | @ -147,7 +201,7 @@ private static final String SERVICE_APP_ID = "invokedemo"; | |||
|     try (DaprClient client = (new DaprClientBuilder()).build()) { | ||||
|       for (String message : args) { | ||||
|         try (Scope scope = tracer.withSpan(span)) { | ||||
|           InvokeServiceRequestBuilder builder = new InvokeServiceRequestBuilder(SERVICE_APP_ID, "echo"); | ||||
|           InvokeServiceRequestBuilder builder = new InvokeServiceRequestBuilder(SERVICE_APP_ID, "proxy_echo"); | ||||
|           InvokeServiceRequest request | ||||
|               = builder.withBody(message).withHttpExtension(HttpExtension.POST).withContext(Context.current()).build(); | ||||
|           client.invokeService(request, TypeRef.get(byte[].class)) | ||||
|  | @ -156,7 +210,7 @@ private static final String SERVICE_APP_ID = "invokedemo"; | |||
|                 return r; | ||||
|               }) | ||||
|               .flatMap(r -> { | ||||
|                 InvokeServiceRequest sleepRequest = new InvokeServiceRequestBuilder(SERVICE_APP_ID, "sleep") | ||||
|                 InvokeServiceRequest sleepRequest = new InvokeServiceRequestBuilder(SERVICE_APP_ID, "proxy_sleep") | ||||
|                     .withHttpExtension(HttpExtension.POST) | ||||
|                     .withContext(r.getContext()).build(); | ||||
|                 return client.invokeMethod(sleepRequest, TypeRef.get(Void.class)); | ||||
|  |  | |||
|  | @ -0,0 +1,70 @@ | |||
| /* | ||||
|  * Copyright (c) Microsoft Corporation. | ||||
|  * Licensed under the MIT License. | ||||
|  */ | ||||
| 
 | ||||
| package io.dapr.examples.tracing; | ||||
| 
 | ||||
| import io.dapr.client.DaprClient; | ||||
| import io.dapr.client.domain.HttpExtension; | ||||
| import io.dapr.client.domain.InvokeServiceRequest; | ||||
| import io.dapr.client.domain.InvokeServiceRequestBuilder; | ||||
| import io.dapr.utils.TypeRef; | ||||
| import io.opentelemetry.context.Context; | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.web.bind.annotation.PostMapping; | ||||
| import org.springframework.web.bind.annotation.RequestAttribute; | ||||
| import org.springframework.web.bind.annotation.RequestBody; | ||||
| import org.springframework.web.bind.annotation.RequestHeader; | ||||
| import org.springframework.web.bind.annotation.RestController; | ||||
| import reactor.core.publisher.Mono; | ||||
| 
 | ||||
| import java.util.Map; | ||||
| 
 | ||||
| /** | ||||
|  * SpringBoot Controller to handle service invocation. | ||||
|  * | ||||
|  * <p>Instrumentation is handled in {@link io.dapr.springboot.OpenTelemetryInterceptor}. | ||||
|  */ | ||||
| @RestController | ||||
| public class TracingDemoMiddleServiceController { | ||||
| 
 | ||||
|   private static final String INVOKE_APP_ID = "tracingdemo"; | ||||
| 
 | ||||
|   /** | ||||
|    * Dapr client. | ||||
|    */ | ||||
|   @Autowired | ||||
|   private DaprClient client; | ||||
| 
 | ||||
|   /** | ||||
|    * Handles the 'echo' method invocation, by proxying a call into another service. | ||||
|    * | ||||
|    * @param context The tracing context for the request. | ||||
|    * @param body The body of the http message. | ||||
|    * @return A message containing the time. | ||||
|    */ | ||||
|   @PostMapping(path = "/proxy_echo") | ||||
|   public Mono<byte[]> echo( | ||||
|       @RequestAttribute(name = "opentelemetry-context") Context context, | ||||
|       @RequestBody(required = false) String body) { | ||||
|     InvokeServiceRequestBuilder builder = new InvokeServiceRequestBuilder(INVOKE_APP_ID, "echo"); | ||||
|     InvokeServiceRequest request | ||||
|         = builder.withBody(body).withHttpExtension(HttpExtension.POST).withContext(context).build(); | ||||
|     return client.invokeMethod(request, TypeRef.get(byte[].class)).map(r -> r.getObject()); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Handles the 'sleep' method invocation, by proxying a call into another service. | ||||
|    * | ||||
|    * @param context The tracing context for the request. | ||||
|    * @return A message containing the time. | ||||
|    */ | ||||
|   @PostMapping(path = "/proxy_sleep") | ||||
|   public Mono<Void> sleep(@RequestAttribute(name = "opentelemetry-context") Context context) { | ||||
|     InvokeServiceRequestBuilder builder = new InvokeServiceRequestBuilder(INVOKE_APP_ID, "sleep"); | ||||
|     InvokeServiceRequest request = builder.withHttpExtension(HttpExtension.POST).withContext(context).build(); | ||||
|     return client.invokeMethod(request, TypeRef.get(byte[].class)).then(); | ||||
|   } | ||||
| 
 | ||||
| } | ||||
|  | @ -22,6 +22,9 @@ import org.apache.commons.cli.Options; | |||
|  * 3. Run in server mode: | ||||
|  * dapr run --app-id tracingdemo --app-port 3000 \ | ||||
|  *   -- java -jar target/dapr-java-sdk-examples-exec.jar io.dapr.examples.tracing.TracingDemoService -p 3000 | ||||
|  * 4. Run middle server: | ||||
|  * dapr run --app-id tracingdemoproxy --app-port 3001 \ | ||||
|  *   -- java -jar target/dapr-java-sdk-examples-exec.jar io.dapr.examples.tracing.TracingDemoService -p 3001 | ||||
|  */ | ||||
| public class TracingDemoService { | ||||
| 
 | ||||
|  |  | |||
|  | @ -53,11 +53,11 @@ public class TracingDemoServiceController { | |||
|    * @return A message containing the time. | ||||
|    */ | ||||
|   @PostMapping(path = "/echo") | ||||
|   public Mono<String> handleMethod(@RequestBody(required = false) byte[] body, | ||||
|   public Mono<String> handleMethod(@RequestBody(required = false) String body, | ||||
|                                    @RequestHeader Map<String, String> headers) { | ||||
|     return Mono.fromSupplier(() -> { | ||||
|       try { | ||||
|         String message = body == null ? "" : new String(body, StandardCharsets.UTF_8); | ||||
|         String message = body == null ? "" : body; | ||||
| 
 | ||||
|         Calendar utcNow = Calendar.getInstance(TimeZone.getTimeZone("GMT")); | ||||
|         String utcNowAsString = DATE_FORMAT.format(utcNow.getTime()); | ||||
|  |  | |||
|  | @ -0,0 +1,22 @@ | |||
| /* | ||||
|  * Copyright (c) Microsoft Corporation. | ||||
|  * Licensed under the MIT License. | ||||
|  */ | ||||
| 
 | ||||
| package io.dapr.springboot; | ||||
| 
 | ||||
| import io.dapr.client.DaprClient; | ||||
| import io.dapr.client.DaprClientBuilder; | ||||
| import org.springframework.context.annotation.Bean; | ||||
| import org.springframework.context.annotation.Configuration; | ||||
| 
 | ||||
| @Configuration | ||||
| public class DaprConfig { | ||||
| 
 | ||||
|   private static final DaprClientBuilder BUILDER = new DaprClientBuilder(); | ||||
| 
 | ||||
|   @Bean | ||||
|   public DaprClient buildDaprClient() { | ||||
|     return BUILDER.build(); | ||||
|   } | ||||
| } | ||||
|  | @ -6,12 +6,9 @@ | |||
| package io.dapr.springboot; | ||||
| 
 | ||||
| import io.opentelemetry.api.OpenTelemetry; | ||||
| import io.opentelemetry.api.trace.Span; | ||||
| import io.opentelemetry.api.trace.Tracer; | ||||
| import io.opentelemetry.context.Context; | ||||
| import io.opentelemetry.context.propagation.TextMapPropagator; | ||||
| import org.jetbrains.annotations.Nullable; | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.stereotype.Component; | ||||
| import org.springframework.web.servlet.HandlerInterceptor; | ||||
| import org.springframework.web.servlet.ModelAndView; | ||||
|  | @ -36,8 +33,6 @@ public class OpenTelemetryInterceptor implements HandlerInterceptor { | |||
|           return carrier.getHeader(key); | ||||
|       } | ||||
|   }; | ||||
|   @Autowired | ||||
|   Tracer tracer; | ||||
| 
 | ||||
|   @Override | ||||
|   public boolean preHandle( | ||||
|  | @ -49,43 +44,15 @@ public class OpenTelemetryInterceptor implements HandlerInterceptor { | |||
|       return true; | ||||
|     } | ||||
| 
 | ||||
|     Span span; | ||||
|     try { | ||||
|     Context context = textFormat.extract(Context.current(), request, HTTP_SERVLET_REQUEST_GETTER); | ||||
|     request.setAttribute("opentelemetry-context", context); | ||||
|       span = tracer.spanBuilder(request.getRequestURI()).setParent(context).startSpan(); | ||||
|       span.setAttribute("handler", "pre"); | ||||
|     } catch (Exception e) { | ||||
|       span = tracer.spanBuilder(request.getRequestURI()).startSpan(); | ||||
|       span.setAttribute("handler", "pre"); | ||||
| 
 | ||||
|       span.addEvent(e.toString()); | ||||
|       span.setAttribute("error", true); | ||||
|     } | ||||
|     request.setAttribute("opentelemetry-span", span); | ||||
|     return true; | ||||
|   } | ||||
| 
 | ||||
|   @Override | ||||
|   public void postHandle( | ||||
|       HttpServletRequest request, HttpServletResponse response, Object handler, | ||||
|       ModelAndView modelAndView) throws Exception { | ||||
|   } | ||||
| 
 | ||||
|   @Override | ||||
|   public void afterCompletion(HttpServletRequest request, HttpServletResponse response, | ||||
|                               Object handler, Exception exception) { | ||||
|     Object contextObject = request.getAttribute("opentelemetry-context"); | ||||
|     Object spanObject = request.getAttribute("opentelemetry-span"); | ||||
|     if ((contextObject == null) || (spanObject == null)) { | ||||
|       return; | ||||
|     } | ||||
|     Context context = (Context) contextObject; | ||||
|     Span span = (Span) spanObject; | ||||
|     span.setAttribute("handler", "afterCompletion"); | ||||
|     final TextMapPropagator textFormat = OpenTelemetry.getGlobalPropagators().getTextMapPropagator(); | ||||
|     textFormat.inject(context, response, HttpServletResponse::addHeader); | ||||
|     span.end(); | ||||
|       ModelAndView modelAndView) { | ||||
|   } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 735 KiB After Width: | Height: | Size: 336 KiB | 
		Loading…
	
		Reference in New Issue