The microprofile-opentracing
quickstart demonstrates the use of the MicroProfile OpenTracing specification in {productName}.
MicroProfile OpenTracing allows users to track requests across service boundaries which is necessary in the microservices architecture. It is based on https://opentracing.io/. The MicroProfile OpenTracing provides a way to instrument services with the distributed tracing function, given an existing distributed tracing system in the environment (e.g. Jaeger or Zipkin).
In this quickstart, we have a collection of CDI beans and REST endpoints that expose functionalities of the MicroProfile OpenTracing specification.
We recommend that you follow the instructions that create the application step by step. However, you can also go right to the completed example which is available in this directory.
mvn archetype:generate \
-DgroupId=org.wildfly.quickstarts \
-DartifactId=microprofile-opentracing \
-DinteractiveMode=false \
-DarchetypeGroupId=org.apache.maven.archetypes \
-DarchetypeArtifactId=maven-archetype-webapp
cd microprofile-opentracing
Open the project in your favourite IDE.
The first thing to do is to setup our dependencies. Add the following section to your
pom.xml
:
<dependencyManagement>
<dependencies>
<!-- importing the microprofile BOM adds MicroProfile specs -->
<dependency>
<groupId>org.wildfly.bom</groupId>
<artifactId>wildfly-microprofile</artifactId>
<version>${version.server.bom}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
where ${version.server.bom}
is the version of your {productName}, in our case
"{productVersion}".
Now we need to add the following dependencies:
<!-- Import the MicroProfile OpenTracing API, we use provided scope as the API is included in the server -->
<dependency>
<groupId>org.eclipse.microprofile.opentracing</groupId>
<artifactId>microprofile-opentracing-api</artifactId>
<scope>provided</scope>
</dependency>
<!-- Import the CDI API, we use provided scope as the API is included in the server -->
<dependency>
<groupId>jakarta.enterprise</groupId>
<artifactId>jakarta.enterprise.cdi-api</artifactId>
<scope>provided</scope>
</dependency>
<!-- Import the Jakarta REST API, we use provided scope as the API is included in the server -->
<dependency>
<groupId>org.jboss.spec.javax.ws.rs</groupId>
<artifactId>jboss-jaxrs-api_2.1_spec</artifactId>
<scope>provided</scope>
</dependency>
All dependencies can have provided scope.
As we are going to be deploying this application to the {productName} server, let’s also add a maven plugin that will simplify the deployment operations (you can replace the generated build section):
<build>
<!-- Set the name of the archive -->
<finalName>${project.artifactId}</finalName>
<plugins>
<!-- Allows to use mvn wildfly:deploy -->
<plugin>
<groupId>org.wildfly.plugins</groupId>
<artifactId>wildfly-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
As this is a Jakarta REST application we need to also create an application class.
Create org.wildfly.quickstarts.microprofile.opentracing.JaxRsApplication
with the
following content:
Note
|
The new file should be created in
src/main/java/org/wildfly/quickstarts/microprofile/opentracing/JaxRsApplication.java .
|
package org.wildfly.quickstarts.microprofile.opentracing;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
@ApplicationPath("/")
public class JaxRsApplication extends Application {
}
Now we are ready to start working with MicroProfile OpenTracing.
To collect the traces from our application we will be using the Jaeger tracing system. To run the Jaeger service we will use its Docker container.
Note
|
If you don’t have Docker installed on your machine please follow the instructions at https://www.docker.com/get-started. |
Run the following command:
docker run --rm --name jaeger -p6831:6831/udp -p16686:16686 jaegertracing/all-in-one:1.16.0
Note
|
This can take a minute. |
Now you can access http://localhost:16686
in your browser to see the Jaeger UI
console.
Now we can start adding our custom spans from our application.
The MicroProfile OpenTracing specification provides an implicit tracing of all JAX-RS resources. That means that an implementation of MicroProfile OpenTracing will automatically:
-
extract the Span context from the incoming JAX-RS request
-
start a new Span on incoming JAX-RS request and close it when the request is completed
-
inject Span context to any outgoing JAX-RS request
-
start a Span for any outgoing JAX-RS request and finish the Span when the request is completed
Let’s create a new JAX-RS resource to demonstrate this. Create a new class
org.wildfly.quickstarts.microprofile.opentracing.TracedResource
:
package org.wildfly.quickstarts.microprofile.opentracing;
import javax.enterprise.context.ApplicationScoped;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
@Path("/hello")
@ApplicationScoped
public class TracedResource {
@GET
@Path("/traced")
@Produces(MediaType.TEXT_PLAIN)
public String hello() {
return "hello";
}
}
That’s it. There is no tracing code included in the application. All requests to this resource will be traced.
Let’s invoke our resource several times, so we can observe the created spans in the Jaeger console:
Build and deploy the application
$ mvn clean package wildfly:deploy
and make a few requests to the TracedResource
by accessing
http://localhost:8080/microprofile-opentracing/hello/traced
in your browser or
by curl http://localhost:8080/microprofile-opentracing/hello/traced
. You will
see the spans created in the Jaeger console (http://localhost:16686
) under the
service microprofile-opentracing.war
.
Congratulations! We already have tracing with no instrumentation in the code. Let’s now dive into how we can fine tune the tracing in our applications.
The @Traced
annotation can be used for:
-
disabling the implicit instrumentation in JAX-RS resources
-
define a custom operation name
-
using tracing explicitly in non JAX-RS classes (see the next section)
To disable implicit tracing you can specify @Traced(false)
on any JAX-RS resource
method to exclude it from distributed traces. Add the following method to the
TracedResource
class:
@GET
@Path("/notTraced")
@Produces(MediaType.TEXT_PLAIN)
@Traced(false)
public String notTraced() {
return "notTraced";
}
And rebuild and deploy the application
$ mvn clean package wildfly:deploy
Note
|
Because of WFLY-13080 it is also necessary to restart the server now (stop the server and rerun it again). |
and make a few requests to the new endpoint by accessing
http://localhost:8080/microprofile-opentracing/hello/notTraced
in your browser or
by curl http://localhost:8080/microprofile-opentracing/hello/notTraced
. You will
see that the spans for these invocations are not created in the Jaeger console
(http://localhost:16686
) under the service microprofile-opentracing.war
. However,
if you repeat some calls to http://localhost:8080/microprofile-opentracing/hello/traced
they will appear in the collected spans again.
To define a custom operation name we can pass it as an operationName
parameter.
Update the TracedResource
class:
@GET
@Path("/traced")
@Produces(MediaType.TEXT_PLAIN)
@Traced(operationName = "hello-operation")
public String hello() {
return "hello";
}
And rebuild and deploy the application
$ mvn clean package wildfly:deploy
Note
|
Because of WFLY-13080 it is also necessary to restart the server now (stop the server and rerun it again). |
and make a few requests to the endpoint by accessing
http://localhost:8080/microprofile-opentracing/hello/traced
in your browser or
by curl http://localhost:8080/microprofile-opentracing/hello/traced
. You will
see that the spans for these invocations are created with the custom name in the
Jaeger console (http://localhost:16686
) under the service
microprofile-opentracing.war
.
By default, only the JAX-RS resources are traced. However, the @Traced
annotation
is a CDI interceptor so we can use it in any CDI bean for more granular tracing
inside the service invocation. Create a new class
org.wildfly.quickstarts.microprofile.opentracing.ExplicitlyTracedBean
:
package org.wildfly.quickstarts.microprofile.opentracing;
import org.eclipse.microprofile.opentracing.Traced;
import javax.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class ExplicitlyTracedBean {
@Traced
public String getHello() {
return "hello";
}
}
Note the explicit @Traced
annotation on the getHello()
method.
And update the TracedResource
to use our newly defined bean:
@Inject
private ExplicitlyTracedBean tracedBean;
@GET
@Path("/cdi-trace")
@Produces(MediaType.TEXT_PLAIN)
public String cdiHello() {
return tracedBean.getHello();
}
Build and redeploy the application
$ mvn clean package wildfly:deploy
Note
|
Because of WFLY-13080 it is also necessary to restart the server now (stop the server and rerun it again). |
and repeat a few requests to the TracedResource
by accessing
http://localhost:8080/microprofile-opentracing/hello/cdi-trace
in your browser or
by curl http://localhost:8080/microprofile-opentracing/hello/cdi-trace
. You will
see the two created spans in the Jaeger console (http://localhost:16686
) under the
service microprofile-opentracing.war
. One for the JAX-RS call and one
for the CDI bean invocation.
For even more granular tracing, the MicroProfile OpenTracing allows you to inject
directly the configured io.opentracing.Tracer
object.
To use io.opentracing.Tracer
in our application we need to add one more maven
dependency providing the OpenTracing API:
<!-- Import the OpenTracing API for the use of io.opentracing.Tracer -->
<dependency>
<groupId>io.opentracing</groupId>
<artifactId>opentracing-api</artifactId>
<version>0.31.0</version>
</dependency>
Now we can update the ExplicitlyTracedBean
:
package org.wildfly.quickstarts.microprofile.opentracing;
import io.opentracing.Span;
import io.opentracing.Tracer;
import org.eclipse.microprofile.opentracing.Traced;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
@ApplicationScoped
public class ExplicitlyTracedBean {
@Inject
private Tracer tracer;
@Traced
public String getHello() {
Span prepareHelloSpan = tracer.buildSpan("prepare-hello").start();
String hello = "hello";
Span processHelloSpan = tracer.buildSpan("process-hello").start();
hello = hello.toUpperCase();
processHelloSpan.finish();
prepareHelloSpan.finish();
return hello;
}
}
Build and redeploy the application
$ mvn clean package wildfly:deploy
Note
|
Because of WFLY-13080 it is also necessary to restart the server now (stop the server and rerun it again). |
and repeat a few requests to the TracedResource
by accessing
http://localhost:8080/microprofile-opentracing/hello/cdi-trace
in your browser or
by curl http://localhost:8080/microprofile-opentracing/hello/cdi-trace
. You will
see now four created spans in the Jaeger console (http://localhost:16686
) under the
service microprofile-opentracing.war
. One for the JAX-RS call, one
for the CDI bean invocation and two manually created spans in the
ExplicitlyTracedBean
.
MicroProfile OpenTracing provides the mechanisms for your application to participate
in the distributed tracing with minimal effort on the application side. The JAX-RS
resources are always traced by default but the specification allows you to control
individual spans directly with the @Traced
interceptor or with the CDI injection
of the io.opentracing.Tracer
.