How to run OpenTelemetry with WildFly Bootable Jar

This article will teach you how to use the OpenTelemetry API in a sample REST Service which uses WildFly Bootable jar technology as runtime.

An overview of OpenTelemetry

OpenTelemetry is a set of APIs and libraries that provide a vendor-neutral and open standard for observability in cloud-native applications. It allows developers to instrument their code and collect data about its performance and behavior, which can then be used to troubleshoot issues and improve the overall quality of the application.

The OpenTelemetry API is the core component of the OpenTelemetry ecosystem. It provides a consistent and uniform interface for interacting with different observability data sources, such as metrics, traces, and logs. This allows developers to easily integrate observability data into their applications, regardless of the underlying technology or platform.

Recommended readings:

Coding a sample Service

The following sample REST Endpoint shows how to send Spans through the injected Tracer Class:

@Path("/simple")
@ApplicationScoped
public class SimpleRESTService {

    @Inject
    private Tracer tracer;

    @GET
    @Path("/xml")
    @Produces(MediaType.APPLICATION_XML)
    public SimpleProperty getPropertyXML ()
    {
        final Span span = tracer.spanBuilder("Preparing XML").startSpan();
        span.makeCurrent();

        SimpleProperty p = new SimpleProperty(UUID.randomUUID().toString(),
                                                      UUID.randomUUID().toString());

        span.addEvent("Built Property");
        span.end();

        return p;
    }

    @GET
    @Path("/json")
    @Produces(MediaType.APPLICATION_JSON)
    public SimpleProperty getPropertyJSON (@Context HttpServletRequest request)
    {
        final Span span = tracer.spanBuilder("Preparing JSON").setAttribute("SESSION", request.getSession().getId())
                .startSpan();
        span.makeCurrent();

        SimpleProperty p = new SimpleProperty(UUID.randomUUID().toString(),
                UUID.randomUUID().toString());

        span.addEvent("Built Property");
        span.end();

        return p;
    }


}

Here is more about the above Class:

  • Firstly, we are creating a Span from the Tracer object through its factory method tracer.spanBuilder.
  • Then, we are optionally adding an Attribute to one of the Spans we are creating. For example, the method getPropertyJSON shows how to add the SessionId attribute to the Span so that you can search traces globally for a specific HTTP Session.
  • A Span has also the ability to add Events which have a time associated with the moment when they are added to the Span. For example, you can use them to record the length of some tasks.
  • Finally, we call the end operation to signal that the Span operation has now ended.

Building the application

In order to build the above application, you need to include the opentelemetry-api dependency in your project. Besides, we will also add the Jakarta EE dependency:

 <dependency>
	    <groupId>io.opentelemetry</groupId>
	    <artifactId>opentelemetry-api</artifactId>
	    <version>${opentelemetry.version}</version>
</dependency>

<dependency>
      <groupId>jakarta.platform</groupId>
      <artifactId>jakarta.jakartaee-api</artifactId>
      <version>${jakartaee.version}</version>
      <scope>provided</scope>    
</dependency>

Then, we will configure WildFly Bootable Jar plugin as follows:

  <build>
    <finalName>${project.artifactId}</finalName>
    <plugins>
      <plugin>
        <groupId>org.wildfly.plugins</groupId>
        <artifactId>wildfly-jar-maven-plugin</artifactId>
        <configuration>
          <feature-pack-location>wildfly@maven(org.jboss.universe:community-universe)#${version.wildfly}</feature-pack-location>
          <layers>
            <layer>jaxrs-server</layer>
            <layer>opentelemetry</layer>
          </layers>
          <excluded-layers>
            <layer>deployment-scanner</layer>
          </excluded-layers>
          <plugin-options>
            <jboss-fork-embedded>${plugin.fork.embedded}</jboss-fork-embedded>
          </plugin-options>
        </configuration>
        <executions>
          <execution>
            <goals>
              <goal>package</goal>
            </goals>
          </execution>
        </executions>
      </plugin>

Please notice that we are using the opentelemetry layers to include in the Bootable Jar the opentelemetry extension and subsystem.

Testing the application

To test the above application, we need a tracing serer. For example, we can start a Jeager instance with Docker:

docker run --rm -d --name jaeger   -p 6831:6831/udp   -p 5778:5778   -p 14250:14250   -p 14268:14268   -p 16686:16686   jaegertracing/all-in-one:1.16

Finally, start the application with:

mvn install wildfly-jar:run

Then, launch some HTTP Requests to the endpoints:

curl http://localhost:8080/rest/simple/json

curl http://localhost:8080/rest/simple/xml

To check the application traces, connect to the Jaeger Console at this URL: http://localhost:16686

Select the “wildfly” application and check the Spans:

opentelemetry tutorial

As you can see from the above picture, we have a Trace Timeline for our Spans. Within it, you can see the SESSION attribute as a Tag in the Span.

Conclusion

In conclusion, the OpenTelemetry API is a powerful and flexible tool for adding observability to cloud-native applications. Its support for distributed tracing, metrics, and logs, as well as its wide range of language and platform support, make it a valuable addition to any developer’s toolkit.

Source code: https://github.com/fmarchioni/practical-enterprise-development/tree/bootable-jakartaee/code/microprofile/telemetry

The source code is an excerpt from the book Practical Enterprise Application Development (Jakarta EE 10 version) that will be launched next week. Stay tuned!

Many thanks to our Red Hat engineer Jean-Francois Denise for helping me in coding the configuration for the Bootable Jar plugin.