See how OpenTracing can be used to create a tracing backend to assist with app instrumentation. We’ll use the new EJB integration and Wildfly Swarm to help.
OpenTracing features more and more framework integrations, allowing for transparent instrumentation of applications with minimal effort. This blog post will show how to use the EJB instrumentation to automatically trace EJB invocations.
For this demo, we’ ll generate a project using the Wildfly Swarm project generator, which allows us to have a seed project with the appropriate OpenTracing support in place. The concrete OpenTracing solution we will use is provided by the Jaeger project, which is also provided as a Wildfly Swarm Fraction.
With that, we’ ll create a simple JAX-RS endpoint with an EJB facet, invoking a set of EJB services in different ways to demonstrate all the features of this integration.
Our application has one endpoint called /order, responsible for receiving requests to place orders in our system. When we call this endpoint, we also call some other EJB services, like
As we are only interested in the tracing parts, we’ ll not implement the business code itself, only the scaffolding.
Our demo project is heavily based on the opentracing-ejb-example from the repository opentracing-contrib/java-ejb. We have also prepared an archive with the final outcome of this demo, which you can use as reference.
To generate the seed project, open the Wildfly Swarm generator and create a project with the «Group ID» io.opentracing.contrib.ejb and «Artifact ID» demo-example. Add the dependencies EJB, CDI, JAX-RS, OpenTracing, and Jaeger.
Click on Generate Project and you’ ll get a ZIP file with the seed project. Uncompress it and add the following dependency to the pom.xml, within the dependencies node and after the WildFly Swarm Fractions dependencies:
It’s now a good time to perform a sanity build, to make sure everything is in place. The first build might take a few minutes:
If it looks good, stop the server with Ctrl+C and let’s start coding our application!
Let’s start by defining a JAX-RS endpoint that also acts as a stateless EJB. This is a common trick to get JAX-RS endpoints to be managed as EJBs, so that they can be invoked via JMX or get monitoring features. Or, in our case, to get traced via EJB interceptors.
This endpoint is where we get our HTTP requests from and where our transaction starts, from the tracing perspective. Once we receive an HTTP request, we call the AccountService#sendNotification method and then the OrderService#processOrderPlacement.
Note that we annotate the class with @Interceptors (OpenTracingInterceptor.class) , which means that all methods on this class are to be traced.
src/main/java/io/opentracing/contrib/ejb/demoexample/Endpoint.java:
Our AccountService is a simple stateless EJB, responsible for sending a notification about the new order to the owner of the account. Here, we could call another service, or send an email, SMS or any other form of message.
As this is a regular EJB, we are able to automatically join the span context from the JAX-RS endpoint, making this call a child span of the main transaction. This is all transparent to you as developer.
Note again that we annotate the bean with @Interceptors (OpenTracingInterceptor.class) . As our interceptor is just like any other EJB interceptor, you could use a ejb-jar.xml to automatically use this inteceptor on all available beans. Whether or not to trace all beans is a per-deployment decision, so, no ejb-jar.xml is provided by the integration.
src/main/java/io/opentracing/contrib/ejb/demoexample/AccountService.java:
Our OrderService is responsible for actually placing the order: it’s where the business knowledge resides. We’ ll later look into details at the InventoryService, but for now, we need to know that this service requires a SpanContext to be explicitly passed. We can get this context from the EJBContext, stored under a context data entry that can be retrieved with the constant io.opentracing.contrib.ejb. OpenTracingInterceptor. SPAN_CONTEXT.
src/main/java/io/opentracing/contrib/ejb/demoexample/OrderService.java:
Our InventoryService is responsible for interfacing with backend systems dealing with inventory control. We don’ t want to block the parent transaction while interacting with those systems, so, we make this an asynchronous EJB. When dealing with asynchronous objects, it’s a good idea to be explicit about the span context, as there are potential concurrency issues when sharing a context between a synchronous and an asynchronous bean.
The OpenTracing EJB integration is able to intercept the method call and detect if there is a span context among the parameters, which is the case of the changeInventory (SpanContext) method. In this situation, the following happens behind the scenes:
Note that the SpanContext passed by the OrderService is not the same as the one received by InventoryService. While this might cause some confusion, we believe this is the right semantic for this use case, as it allows for a complete tracing picture, without any explicit tracing code, apart from passing the context around.
src/main/java/io/opentracing/contrib/ejb/demoexample/InventoryService.java:
And finally, our last service, InventoryNotificationService: in this case, we notify another set of backend systems that a new order has been placed. Again, this is an asynchronous EJB and works like the one above, but additionally, we wanted to manually create a «business span», called sendNotification. This method could send several notifications, wrapping each one into a span of its own. As we manually started it, we manually finish it as well.
src/main/java/io/opentracing/contrib/ejb/demoexample/ InventoryNotificationService.java:
Now, let’s do a final sanity check and see if everything is in the right place: mvn wildfly-swarm: run. As before, the final message should be WildFly Swarm is Ready. Hit Ctrl+C and let’s set up our tracing backend.
Instrumenting our code is one part of the story. The other part is to plug in an actual OpenTracing implementation that is capable of capturing the spans and submitting them to a backend service. For our demo, we’ ll use Jaeger. If you don’ t have a Jaeger server running yet, one can be started via Docker as follows:
Now that we have our code ready and a tracing backend, let’s start Wildfly Swarm passing a service name, which is the only property required by the Jaeger client. By default, Jaeger’s Java tracer will attempt to send traces via UDP to a Jaeger Agent located on the local machine. If you are using a different architecture, refer to the Jaeger’s documentation on how to use environment variables to configure the client, or refer to the Jaeger’s fraction for Wildfly Swarm.
Домой
United States
USA — software OpenTracing EJB Instrumentation on Wildfly Swarm OpenTracing EJB Instrumentation on Wildfly Swarm