Tracing Journey For Akka Family
By: Hoda Alemi
Tags: Opentracing, Tracing, Akka Http, Scala, Akka Actor

Within ING, every month we dedicate one day to improve our developement skills. I decided to delve into tracing and the underlying implementation. In this article, I will explain tracing in a Rest API which is built on top of Akka HTTP and Akka-Actors framework and is written in Scala. The API has the functionality of registering orders.

OpenTracing and Terminologies

OpenTracing is an open (language agnostic) standard for distributed tracing. OpenTracing consist of an API specification, frameworks and libraries which enables your application with the instrumentation for distributed tracing.

Distributed request tracing represents a transaction’s flow within different components of your software application. It also helps you to identify the bottlenecks of your application and to understand what happens to a request as it transits through different operations within your application flow or microservices. Let’s first delve into some of the terminologies in distributed tracing:

Trace: is a story of a transaction or workflow that passes on multiple operations or services. Every trace has a unique Trace ID that is shared within set of spans.

is a story of a transaction or workflow that passes on multiple operations or services. Every trace has a unique Trace ID that is shared within set of spans. Tracer: is the actual implementation which records and publishes the spans.

is the actual implementation which records and publishes the spans. Span: every operational unit or service which is named and timed and is part of a trace.

every operational unit or service which is named and timed and is part of a trace. Child span: is a span which is subsequent of another span and can be nested.

is a span which is subsequent of another span and can be nested. Span context: contains trace information across service operation boundaries, including the time that it passes from service to service

The below diagram shows the span relationships of a trace in OrderRegistryAPI:

Tracing Client and Backend

As explained, the tracer is the actual implementation which has the responsibility of recording and publishing the spans. There are different implementations for a tracer. In this example, I am going to use the implementation from Jaeger library. Jaeger is an open source, end-to-end distributed tracing system. Different components of Jaeger are shown below:

Technically the flow is as following, you instrument your application with OpenTracing and the Jaeger client library. The tracer reports the trace to the Jaeger backend. And finally, the trace can be retrieved in the Jaeger user interface.

Implementation

The OrderRegistry is a Rest API written in Scala with the simple functionality of registering orders. This API has four endpoints:

GET /orders: Retrieves all available orders

GET /orders/<order-id>: Retrieves a specific order

POST /orders: Submits an order

DELETE /orders/<order-id>: Cancels an order by providing the order-id

The tracer has been initialized as below:

Each of the AKKA HTTP routes are wrapped with a withTrace function that creates a span per operation.

Below it is shown how the withTrace function is used for the get /orders route.

After instrumentation of the code with tracing, you can run Jaeger backend via a docker image as follows:

Jaeger graphical user interface will be available on http://localhost:16686. Now if we make some requests to the OrderRegistryAPI, we can find our request traces in the Jaeger GUI. The below figure demonstrates Jaeger GUI with three traces to the orderRegistryAPI.