git clone https://github.com/openliberty/guide-microprofile-opentracing-jaeger.git
cd guide-microprofile-opentracing-jaeger
Enabling distributed tracing in microservices with Jaeger
Prerequisites:
Explore how to enable and customize tracing of JAX-RS and non-JAX-RS endpoint methods by using MicroProfile OpenTracing and Jaeger.
What you’ll learn
You will learn how to enable automatic tracing for JAX-RS endpoint methods and create custom tracers for non-JAX-RS endpoint methods by using MicroProfile OpenTracing.
OpenTracing is a standard API for instrumenting microservices for distributed tracing. Distributed tracing helps troubleshoot microservices by examining and logging requests as they propagate through a distributed system, allowing developers to tackle the otherwise difficult task of debugging these requests. Without a distributed tracing system in place, analyzing the workflows of operations becomes difficult, particularly in regard to pinpointing when and by whom a request is received or when a response is sent back.
Tracer
and Span
are two critical types in the OpenTracing specification. The Span
type is the primary building block of a distributed trace, representing an individual unit of work done in a distributed system. The Trace
type in OpenTracing can be thought of as a directed acyclic graph (DAG) of Spans
, where the edges between Spans
are called References. The Tracer
interface creates Spans
and Traces
and understands how to serialize and deserialize their metadata across process boundaries.
MicroProfile OpenTracing enables distributed tracing in microservices. The MicroProfile OpenTracing specification doesn’t address the problem of defining, implementing, or configuring the underlying distributed tracing system. Rather, the specification makes it easier to instrument services with distributed tracing given an existing distributed tracing system.
Jaeger is an open source distributed tracing system that is compatible with the OpenTracing specification. Jaeger also provides an implementation of Tracer
in the client package that is compatible with MicroProfile OpenTracing.
You’ll configure the provided inventory
and system
services to use Jaeger for distributed tracing with MicroProfile OpenTracing.
You’ll run these services in two separate JVMs made of two server instances to demonstrate tracing in a distributed environment.
If all the components were run on a single server, then any logging software would be sufficient.
Additional prerequisites
Before you begin, deploy the Jaeger all-in-one executable file to start the Jaeger tracing system. The Jaeger all-in-one executable file is configured for quick local testing. You can find information about the Jaeger server and instructions for starting the all-in-one executable file in the Jaeger documentation.
Before you proceed, make sure that your Jaeger server is up and running. Jaeger can be found at the http://localhost:16686 URL.
Getting started
The fastest way to work through this guide is to clone the Git repository and use the projects that are provided inside:
The start
directory contains the starting project that you will build upon.
The finish
directory contains the finished project that you will build.
Before you begin, make sure you have all the necessary prerequisites.
Try what you’ll build
The finish
directory in the root of this guide contains the finished application. Give it a try before you proceed.
Open a command-line session and navigate to the finish/inventory
directory. Run the following Maven goal to build the inventory
service and deploy it to Open Liberty:
mvn liberty:run
Open another command-line session and navigate to the finish/system
directory. Run the following Maven goal to build the system
service and deploy it to Open Liberty:
mvn liberty:run
After you see the following message in both command-line sessions, both of your services are ready:
The defaultServer server is ready to run a smarter planet.
Make sure that your Jaeger server is running and point your browser to the http://localhost:9081/inventory/systems/localhost URL. When you visit this endpoint, you make two GET HTTP requests, one to the system
service and one to the inventory
service. Both of these requests are configured to be traced, so a new trace is recorded in Jaeger.
To view the traces, go to the http://localhost:16686 URL. You can view the traces for the inventory or system services under the Search tab. Select the services in the Select a service menu and click the Find Traces button at the end of the section.
If you only see the jaeger-query option listed in the dropdown, you might need to wait a little longer and refresh the page to see the application services.
View the traces for inventory
. You’ll see the following trace:
The trace has four spans, three from inventory and one from system. Click the trace to view its details. Under Service & Operation, you see the spans in this trace. You can inspect each span by clicking it to reveal more detailed information, such as the time at which a request was received and the time at which a response was sent back.
Verify that there are three spans from inventory
and one span from system
:
After you’re finished reviewing the application, stop the Open Liberty servers by pressing CTRL+C
in the command-line sessions where you ran the system and inventory services. Alternatively, you can run the following goals from the finish
directory in another command-line session:
mvn -pl system liberty:stop mvn -pl inventory liberty:stop
Building the application
You need to start the services to see basic traces appear in Jaeger.
When you run Open Liberty in dev mode, the server listens for file changes and automatically recompiles and deploys your updates whenever you save a new change.
Open a command-line session and navigate to the start/inventory
directory.
Run the following Maven goal to start the inventory
service in dev mode:
mvn liberty:dev
Open a command-line session and navigate to the start/system
directory.
Run the following Maven goal to start the system
service in dev mode:
mvn liberty:dev
After you see the following message, your application server in dev mode is ready:
************************************************************** * Liberty is running in dev mode.
Dev mode holds your command-line session to listen for file changes. Open another command-line session to continue, or open the project in your editor.
When the servers start, you can find the system
and inventory
services at the following URLs:
Enabling existing Tracer implementation
To collect traces across your systems, you need to implement the OpenTracing Tracer
interface. Jaeger provides a Tracer
implementation for the Jaeger server in the jaeger-client
package.
This package is already added as a dependency for you in your pom.xml
file. It’s downloaded and installed automatically into each service when you run a Maven build.
Configuring the Jaeger client
In a development environment, it is important that every trace is sampled. When every trace is sampled, all spans are available in the Jaeger UI.
The JAEGER_SAMPLER_TYPE
and JAEGER_SAMPLER_PARAM
environment variables are set as Open Liberty configuration properties
to sample all traces.
The const
value for JAEGER_SAMPLER_TYPE
environment variable configures the Jaeger client sampler to make the same sampling decision for each trace, based on the sampler parameter. If the sampler parameter is 1, it samples all traces. If the sampler parameter is 0, it doesn’t sample any traces.
The 1
value for JAEGER_SAMPLER_PARAM
variable configures the Jaeger sampler to sample all traces.
In a production environment, this configuration might cause a lot of overhead on the application and a lower sampling rate can be used. The different values for client sampling configuration can be found in the sampling documentation.
Similarly, in a production environment, Jaeger might not be running in the same host as the application. In this case, set the hostname of the Jaeger server to the JAEGER_AGENT_HOST
environment variable and set the port that communicates with the Jaeger host to the JAEGER_AGENT_PORT
environment variable.
You can view the configuration environment variables at the Jaeger Java client documentation.
pom.xml
1<?xml version='1.0' encoding='utf-8'?>
2<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
3http://maven.apache.org/xsd/maven-4.0.0.xsd">
4 <modelVersion>4.0.0</modelVersion>
5
6 <groupId>io.openliberty.guides</groupId>
7 <artifactId>guide-microprofile-opentracing-jaeger-inventory</artifactId>
8 <version>1.0-SNAPSHOT</version>
9 <packaging>war</packaging>
10
11 <properties>
12 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
13 <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
14 <maven.compiler.source>1.8</maven.compiler.source>
15 <maven.compiler.target>1.8</maven.compiler.target>
16
17 <!-- OpenLiberty runtime -->
18 <liberty.var.system.http.port>9080</liberty.var.system.http.port>
19 <liberty.var.default.http.port>9081</liberty.var.default.http.port>
20 <liberty.var.default.https.port>9444</liberty.var.default.https.port>
21
22 <!-- Jaeger configuration -->
23 <!-- tag::jaegerClientConfig[] -->
24 <!-- tag::samplerTypeConfig[] -->
25 <liberty.env.JAEGER_SAMPLER_TYPE>const</liberty.env.JAEGER_SAMPLER_TYPE>
26 <!-- end::samplerTypeConfig[] -->
27 <!-- tag::samplerParamConfig[] -->
28 <liberty.env.JAEGER_SAMPLER_PARAM>1</liberty.env.JAEGER_SAMPLER_PARAM>
29 <!-- end::samplerParamConfig[] -->
30 <!-- end::jaegerClientConfig[] -->
31 </properties>
32
33 <dependencies>
34 <!-- Provided dependencies -->
35 <dependency>
36 <groupId>jakarta.platform</groupId>
37 <artifactId>jakarta.jakartaee-api</artifactId>
38 <version>9.1.0</version>
39 <scope>provided</scope>
40 </dependency>
41 <dependency>
42 <groupId>org.eclipse.microprofile</groupId>
43 <artifactId>microprofile</artifactId>
44 <version>5.0</version>
45 <type>pom</type>
46 <scope>provided</scope>
47 </dependency>
48 <!-- For Jaeger -->
49 <!-- tag::jaeger[] -->
50 <dependency>
51 <groupId>io.jaegertracing</groupId>
52 <artifactId>jaeger-client</artifactId>
53 <version>1.7.0</version>
54 </dependency>
55 <dependency>
56 <groupId>org.slf4j</groupId>
57 <artifactId>slf4j-api</artifactId>
58 <version>1.7.33</version>
59 </dependency>
60 <dependency>
61 <groupId>org.slf4j</groupId>
62 <artifactId>slf4j-jdk14</artifactId>
63 <version>1.7.33</version>
64 </dependency>
65 <!-- end::jaeger[] -->
66 <!-- For tests -->
67 <dependency>
68 <groupId>org.junit.jupiter</groupId>
69 <artifactId>junit-jupiter</artifactId>
70 <version>5.8.2</version>
71 <scope>test</scope>
72 </dependency>
73 <dependency>
74 <groupId>org.jboss.resteasy</groupId>
75 <artifactId>resteasy-client</artifactId>
76 <version>6.0.0.Final</version>
77 <scope>test</scope>
78 </dependency>
79 <dependency>
80 <groupId>org.jboss.resteasy</groupId>
81 <artifactId>resteasy-json-binding-provider</artifactId>
82 <version>6.0.0.Final</version>
83 <scope>test</scope>
84 </dependency>
85 <dependency>
86 <groupId>org.glassfish</groupId>
87 <artifactId>jakarta.json</artifactId>
88 <version>2.0.1</version>
89 <scope>test</scope>
90 </dependency>
91 </dependencies>
92
93 <build>
94 <finalName>${project.artifactId}</finalName>
95 <plugins>
96 <plugin>
97 <groupId>org.apache.maven.plugins</groupId>
98 <artifactId>maven-war-plugin</artifactId>
99 <version>3.3.2</version>
100 </plugin>
101
102 <!-- Liberty plugin -->
103 <plugin>
104 <groupId>io.openliberty.tools</groupId>
105 <artifactId>liberty-maven-plugin</artifactId>
106 <version>3.7.1</version>
107 </plugin>
108
109 <!-- Plugin to run unit tests -->
110 <plugin>
111 <groupId>org.apache.maven.plugins</groupId>
112 <artifactId>maven-surefire-plugin</artifactId>
113 <version>2.22.2</version>
114 </plugin>
115
116 <!-- Plugin to run functional tests -->
117 <plugin>
118 <groupId>org.apache.maven.plugins</groupId>
119 <artifactId>maven-failsafe-plugin</artifactId>
120 <version>2.22.2</version>
121 <configuration>
122 <systemPropertyVariables>
123 <sys.http.port>${liberty.var.system.http.port}</sys.http.port>
124 <inv.http.port>${liberty.var.default.http.port}</inv.http.port>
125 </systemPropertyVariables>
126 </configuration>
127 </plugin>
128 </plugins>
129 </build>
130</project>
Enabling and disabling distributed tracing
The MicroProfile OpenTracing feature enables tracing of all JAX-RS endpoint methods by default. To further control and customize these traces, use the @Traced
annotation to enable and disable tracing of particular methods. You can also inject a custom Tracer
object to create and customize spans.
This feature is already enabled in the inventory
and system
configuration files.
Enabling distributed tracing without code instrumentation
Because tracing of all JAX-RS endpoint methods is enabled by default, you only need to enable the MicroProfile OpenTracing
feature in the server.xml
file to see some basic traces in Jaeger.
The OpenTracing API is exposed as a third-party API in Open Liberty. To add the visibility of OpenTracing APIs to the application, add third-party
to the types of API packages that this class loader supports. Instead of explicitly configuring a list of API packages that includes third-party
, set the +third-party
value to the apiTypeVisibility
attribute in the classLoader
configuration. This configuration adds third-party
to the default list of API package types that are supported.
server.xml
1<server description="Sample Liberty server">
2
3 <featureManager>
4 <feature>restfulWS-3.0</feature>
5 <feature>jsonb-2.0</feature>
6 <feature>jsonp-2.0</feature>
7 <feature>cdi-3.0</feature>
8 <feature>mpConfig-3.0</feature>
9 <!-- tag::mpOpenTracing[] -->
10 <feature>mpOpenTracing-3.0</feature>
11 <!-- end::mpOpenTracing[] -->
12 </featureManager>
13
14 <httpEndpoint httpPort="${default.http.port}" httpsPort="${default.https.port}"
15 id="defaultHttpEndpoint" host="*" />
16
17 <webApplication location="guide-microprofile-opentracing-jaeger-inventory.war" contextRoot="/">
18 <!-- enable visibility to third party apis -->
19 <!-- tag::thirdParty[] -->
20 <classloader apiTypeVisibility="+third-party"/>
21 <!-- end::thirdParty[] -->
22 </webApplication>
23
24</server>
Make sure that your services are running. Then, point your browser to any of the services' endpoints and check your Jaeger server for traces.
Enabling explicit distributed tracing
Use the @Traced
annotation to define explicit span creation for specific classes and methods. If you place the annotation on a class, then the annotation is automatically applied to all methods within that class. If you place the annotation on a method, then the annotation overrides the class annotation if one exists.
The @Traced
annotation can be configured with the following two parameters:
-
The
value=[true|false]
parameter indicates whether a particular class or method is traced. For example, while all JAX-RS endpoint methods are traced by default, you can disable their tracing by using the@Traced(false)
annotation. This parameter is set totrue
by default. -
The
operationName=<Span name>
parameter indicates the name of the span that is assigned to the method that is traced. If you omit this parameter, the span is named with the<package name>.<class name>.<method name>
format. If you use this parameter at a class level, then all methods within that class have the same span name unless they are explicitly overridden by another@Traced
annotation.
Replace theInventoryManager
class.inventory/src/main/java/io/openliberty/guides/inventory/InventoryManager.java
InventoryManager.java
Enable tracing of the list()
non-JAX-RS endpoint method by updating @Traced
as shown.
Go to the http://localhost:9081/inventory/systems URL and check your Jaeger server at the http://localhost:16686 URL. If you have the Jaeger UI open from a previous step, refresh the page. Select the inventory
traces and click the Find Traces button.
You see a new trace record that is two spans long. One span is for the listContents()
JAX-RS endpoint method in the InventoryResource
class, and the other span is for the list()
method in the InventoryManager
class.
Verify that you see the following spans:
InventoryResource.java
Disable automatic distributed tracing
You can use the @Traced
annotation with a value of false
to disable automatic distributed tracing of JAX-RS endpoint methods.
Replace theInventoryResource
class.inventory/src/main/java/io/openliberty/guides/inventory/InventoryResource.java
InventoryResource.java
Disable tracing of the listContents()
JAX-RS endpoint method by setting @Traced(false)
.
Go to the http://localhost:9081/inventory/systems URL and check your Jaeger server at the http://localhost:16686 URL. If you have the Jaeger UI open from a previous step, refresh the page. Select the inventory
traces and click the Find Traces button. You see a new trace record that is just one span long for the remaining list()
method in the InventoryManager
class.
Verify that you see the following span:
InventoryManager.java
Injecting a custom Tracer object
The MicroProfile OpenTracing specification also makes the underlying OpenTracing Tracer
instance available for use. You can access the configured Tracer
by injecting it into a bean by using the @Inject
annotation from the Contexts and Dependency Injections API.
Inject the Tracer
object into the inventory/src/main/java/io/openliberty/guides/inventory/InventoryManager.java
file. Then, use it to define a new child scope in the add()
call.
Replace theInventoryManager
class.inventory/src/main/java/io/openliberty/guides/inventory/InventoryManager.java
InventoryManager.java
This try
block is called a try-with-resources
statement, meaning that the childScope
object is closed at the end of the statement. It’s good practice to define custom spans inside such statements. Otherwise, any exceptions that are thrown before the span closes will leak the active span.
Go to the http://localhost:9081/inventory/systems/localhost URL and check your Jaeger server at the http://localhost:16686
URL.
If you have the Jaeger UI open from a previous step, refresh the page.
Select the inventory
traces and click the Find Traces button.
Verify that there are three spans from inventory
and one span from system
:
This simple example shows what you can do with the injected Tracer
object. More configuration options are available to you, including setting a timestamp for when a span was created and destroyed. However, these options require an implementation of their own, which doesn’t come as a part of the Jaeger user feature that is provided. In a real-world scenario, implement all the OpenTracing interfaces that you deem necessary, which might include the SpanBuilder
interface. You can use this interface for span creation and customization, including setting timestamps.
SystemResource.java
InventoryResource.java
Testing the services
No automated tests are provided to verify the correctness of the traces. Manually verify these traces by viewing them on the Jaeger server.
A few tests are included for you to test the basic functionality of the services. If a test failure occurs, then you might have introduced a bug into the code.
Running the tests
Since you started Open Liberty in dev mode, run the tests for the system and inventory services by pressing the enter/return
key in the command-line sessions where you started the services.
When you are done checking out the services, exit dev mode by pressing CTRL+C
in the shell sessions where you ran the system
and inventory
services, or by typing q
and then pressing the enter/return key
.
Finally, stop the Jaeger
service that you started in the Additional prerequisites section.
Great work! You’re done!
You just used MicroProfile OpenTracing in Open Liberty to customize how and which traces are delivered to Jaeger.
Try out one of the related MicroProfile guides. These guides demonstrate more technologies that you can learn to expand on what you built in this guide.
Guide Attribution
Enabling distributed tracing in microservices with Jaeger by Open Liberty is licensed under CC BY-ND 4.0
Prerequisites:
Nice work! Where to next?
What did you think of this guide?
Thank you for your feedback!
What could make this guide better?
Raise an issue to share feedback
Create a pull request to contribute to this guide
Need help?
Ask a question on Stack Overflow