Tracing in a modern world

In the past months and years, multiple tracing solutions have originated that aim to minimize the performance impact on the system.

OpenTracing is standard, vendor-neutral tracing technology that is part of the Cloud Native Computing Foundation. It defines the concepts and semantics of traces and supports tracing in distributed applications. It is implemented by multiple tracing technologies such as Zipkin, Jaeger, or Hawkular.

A hierarchical trace consists of several spans, similar to the ones shown in the previous figures. A span can be a child of, or follow, another span.

In the previous example, the car manufacture component span is a child of the load balancer span. The persistence span follows the client span since their invocations happen sequentially.

An OpenTracing API span includes a time span, an operation name, context information, as well as optional sets of tags and logs. The operation names and tags are somewhat similar to Prometheus metric names and labels described earlier in the Enter Prometheus section. Logs describe information such as span messages.

An example for a single span is createCar with the tags color=RED and engine=DIESEL, as well as a log message field Car successfully created.

The following code snippet shows an example of using the OpenTracing Java API in the car manufacture application. It supports Java's try-with-resource feature.

import io.opentracing.ActiveSpan;
import io.opentracing.Tracer;

@Stateless
public class CarManufacturer {

    @Inject
    Tracer tracer;

    public Car manufactureCar(Specification spec) {
        try (ActiveSpan span = tracer.buildSpan("createCar")
                .withTag("color", spec.getColor().name())
                .withTag("engine", spec.getEngine().name())
                .startActive()) {

            // perform business logic

            span.log("Car successfully created");
        }
    }
}

The created span starts actively and is added as a child to a potentially existing parent span. The Tracer is produced by a CDI producer that depends on the specific OpenTracing implementation.

Obviously, this approach obfuscates the code a lot and should be moved to cross-cutting components, such as interceptors. Tracing interceptor bindings can decorate methods and extract information about method names and parameters.

Depending on the desired information included in tracing spans, the interceptor binding can be enhanced to provide further information, such as the operation name.

The following code snippet shows a business method decorated with an interceptor binding that adds tracing in a lean way. Implementing the interceptor is left as an exercise for the reader:

@Stateless
public class CarManufacturer {

    ...

    @Traced(operation = "createCar")
    public Car manufactureCar(Specification spec) {
        // perform business logic
    }
}

The traced information is carried into subsequent applications via span contexts and carriers. They enable participating applications to add their tracing information as well.

The gathered data can be extracted via the used OpenTracing implementation. There are filter and interceptor implementations available for technology such as JAX-RS resources and clients that transparently add the required debug information to the invocations, for example, using HTTP headers.

This way of tracing impacts the system's performance way less than traditional logging. It defines the exact steps and systems that instrument the business logic flow. However, as mentioned before, there needs to be a business requirement to implement a tracing solution.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.133.130.199