Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -45,7 +45,7 @@ public static void main(String[] args) throws Exception {
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))
Expand All @@ -54,7 +54,7 @@ public static void main(String[] args) throws Exception {
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));
Expand Down
74 changes: 64 additions & 10 deletions examples/src/main/java/io/dapr/examples/tracing/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down Expand Up @@ -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:

Expand Down Expand Up @@ -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());
Expand All @@ -95,7 +97,7 @@ public class TracingDemoServiceController {

// Handles the request by printing message.
System.out.println(
"Server: " + message + " @ " + utcNowAsString + " and metadata: " + metadataString);
"Server: " + message + " @ " + utcNowAsString + " and metadata: " + metadataString);

return utcNowAsString;
} catch (Exception e) {
Expand Down Expand Up @@ -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());
Expand All @@ -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))
Expand All @@ -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));
Expand Down
Original file line number Diff line number Diff line change
@@ -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();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
* 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 {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
22 changes: 22 additions & 0 deletions examples/src/main/java/io/dapr/springboot/DaprConfig.java
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -36,8 +33,6 @@ public String get(@Nullable HttpServletRequest carrier, String key) {
return carrier.getHeader(key);
}
};
@Autowired
Tracer tracer;

@Override
public boolean preHandle(
Expand All @@ -49,43 +44,15 @@ public boolean preHandle(
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);
Context context = textFormat.extract(Context.current(), request, HTTP_SERVLET_REQUEST_GETTER);
request.setAttribute("opentelemetry-context", context);
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 modified examples/src/main/resources/img/zipkin-details.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.