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.
|
* 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.
|
* Starts the invoke client.
|
||||||
|
|
@ -45,7 +45,7 @@ public class InvokeClient {
|
||||||
try (DaprClient client = (new DaprClientBuilder()).build()) {
|
try (DaprClient client = (new DaprClientBuilder()).build()) {
|
||||||
for (String message : args) {
|
for (String message : args) {
|
||||||
try (Scope scope = span.makeCurrent()) {
|
try (Scope scope = span.makeCurrent()) {
|
||||||
InvokeServiceRequestBuilder builder = new InvokeServiceRequestBuilder(SERVICE_APP_ID, "echo");
|
InvokeServiceRequestBuilder builder = new InvokeServiceRequestBuilder(SERVICE_APP_ID, "proxy_echo");
|
||||||
InvokeServiceRequest request
|
InvokeServiceRequest request
|
||||||
= builder.withBody(message).withHttpExtension(HttpExtension.POST).withContext(Context.current()).build();
|
= builder.withBody(message).withHttpExtension(HttpExtension.POST).withContext(Context.current()).build();
|
||||||
client.invokeMethod(request, TypeRef.get(byte[].class))
|
client.invokeMethod(request, TypeRef.get(byte[].class))
|
||||||
|
|
@ -54,7 +54,7 @@ public class InvokeClient {
|
||||||
return r;
|
return r;
|
||||||
})
|
})
|
||||||
.flatMap(r -> {
|
.flatMap(r -> {
|
||||||
InvokeServiceRequest sleepRequest = new InvokeServiceRequestBuilder(SERVICE_APP_ID, "sleep")
|
InvokeServiceRequest sleepRequest = new InvokeServiceRequestBuilder(SERVICE_APP_ID, "proxy_sleep")
|
||||||
.withHttpExtension(HttpExtension.POST)
|
.withHttpExtension(HttpExtension.POST)
|
||||||
.withContext(r.getContext()).build();
|
.withContext(r.getContext()).build();
|
||||||
return client.invokeMethod(sleepRequest, TypeRef.get(Void.class));
|
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.
|
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:
|
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)
|
* InvokeClient (Invokes the exposed methods from TracingDemoService)
|
||||||
|
|
||||||
Also consider [getting started with observability in Dapr](https://github.com/dapr/quickstarts/tree/master/observability).
|
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).
|
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:
|
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 {
|
public class TracingDemoServiceController {
|
||||||
///...
|
///...
|
||||||
@PostMapping(path = "/echo")
|
@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) {
|
@RequestHeader Map<String, String> headers) {
|
||||||
return Mono.fromSupplier(() -> {
|
return Mono.fromSupplier(() -> {
|
||||||
try {
|
try {
|
||||||
String message = body == null ? "" : new String(body, StandardCharsets.UTF_8);
|
String message = body == null ? "" : body;
|
||||||
|
|
||||||
Calendar utcNow = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
|
Calendar utcNow = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
|
||||||
String utcNowAsString = DATE_FORMAT.format(utcNow.getTime());
|
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.
|
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
|
```java
|
||||||
public class InvokeClient {
|
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 {
|
public static void main(String[] args) throws Exception {
|
||||||
Tracer tracer = OpenTelemetryConfig.createTracer(InvokeClient.class.getCanonicalName());
|
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()) {
|
try (DaprClient client = (new DaprClientBuilder()).build()) {
|
||||||
for (String message : args) {
|
for (String message : args) {
|
||||||
try (Scope scope = tracer.withSpan(span)) {
|
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
|
InvokeServiceRequest request
|
||||||
= builder.withBody(message).withHttpExtension(HttpExtension.POST).withContext(Context.current()).build();
|
= builder.withBody(message).withHttpExtension(HttpExtension.POST).withContext(Context.current()).build();
|
||||||
client.invokeService(request, TypeRef.get(byte[].class))
|
client.invokeService(request, TypeRef.get(byte[].class))
|
||||||
|
|
@ -156,7 +210,7 @@ private static final String SERVICE_APP_ID = "invokedemo";
|
||||||
return r;
|
return r;
|
||||||
})
|
})
|
||||||
.flatMap(r -> {
|
.flatMap(r -> {
|
||||||
InvokeServiceRequest sleepRequest = new InvokeServiceRequestBuilder(SERVICE_APP_ID, "sleep")
|
InvokeServiceRequest sleepRequest = new InvokeServiceRequestBuilder(SERVICE_APP_ID, "proxy_sleep")
|
||||||
.withHttpExtension(HttpExtension.POST)
|
.withHttpExtension(HttpExtension.POST)
|
||||||
.withContext(r.getContext()).build();
|
.withContext(r.getContext()).build();
|
||||||
return client.invokeMethod(sleepRequest, TypeRef.get(Void.class));
|
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:
|
* 3. Run in server mode:
|
||||||
* dapr run --app-id tracingdemo --app-port 3000 \
|
* dapr run --app-id tracingdemo --app-port 3000 \
|
||||||
* -- java -jar target/dapr-java-sdk-examples-exec.jar io.dapr.examples.tracing.TracingDemoService -p 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 {
|
public class TracingDemoService {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,11 +53,11 @@ public class TracingDemoServiceController {
|
||||||
* @return A message containing the time.
|
* @return A message containing the time.
|
||||||
*/
|
*/
|
||||||
@PostMapping(path = "/echo")
|
@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) {
|
@RequestHeader Map<String, String> headers) {
|
||||||
return Mono.fromSupplier(() -> {
|
return Mono.fromSupplier(() -> {
|
||||||
try {
|
try {
|
||||||
String message = body == null ? "" : new String(body, StandardCharsets.UTF_8);
|
String message = body == null ? "" : body;
|
||||||
|
|
||||||
Calendar utcNow = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
|
Calendar utcNow = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
|
||||||
String utcNowAsString = DATE_FORMAT.format(utcNow.getTime());
|
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;
|
package io.dapr.springboot;
|
||||||
|
|
||||||
import io.opentelemetry.api.OpenTelemetry;
|
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.Context;
|
||||||
import io.opentelemetry.context.propagation.TextMapPropagator;
|
import io.opentelemetry.context.propagation.TextMapPropagator;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.web.servlet.HandlerInterceptor;
|
import org.springframework.web.servlet.HandlerInterceptor;
|
||||||
import org.springframework.web.servlet.ModelAndView;
|
import org.springframework.web.servlet.ModelAndView;
|
||||||
|
|
@ -36,8 +33,6 @@ public class OpenTelemetryInterceptor implements HandlerInterceptor {
|
||||||
return carrier.getHeader(key);
|
return carrier.getHeader(key);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@Autowired
|
|
||||||
Tracer tracer;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean preHandle(
|
public boolean preHandle(
|
||||||
|
|
@ -49,43 +44,15 @@ public class OpenTelemetryInterceptor implements HandlerInterceptor {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Span span;
|
|
||||||
try {
|
|
||||||
Context context = textFormat.extract(Context.current(), request, HTTP_SERVLET_REQUEST_GETTER);
|
Context context = textFormat.extract(Context.current(), request, HTTP_SERVLET_REQUEST_GETTER);
|
||||||
request.setAttribute("opentelemetry-context", context);
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postHandle(
|
public void postHandle(
|
||||||
HttpServletRequest request, HttpServletResponse response, Object handler,
|
HttpServletRequest request, HttpServletResponse response, Object handler,
|
||||||
ModelAndView modelAndView) throws Exception {
|
ModelAndView modelAndView) {
|
||||||
}
|
|
||||||
|
|
||||||
@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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 735 KiB After Width: | Height: | Size: 336 KiB |
Loading…
Reference in New Issue