Micronaut Tracing

Adds Distributed Tracing Support

Version:

1 Introduction

When operating Microservices in production it can be challenging to troubleshoot interactions between Microservices in a distributed architecture.

To solve this problem, a way to visualize interactions between Microservices in a distributed manner can be critical. Currently, there are various distributed tracing solutions, the most popular of which are Zipkin and Jaeger, both of which provide different levels of support for the Open Tracing API.

Micronaut features integration with both Zipkin and Jaeger (via the Open Tracing API).

Tracing Annotations

The io.micronaut.tracing.annotation package contains annotations that can be declared on methods to create new spans or continue existing spans.

The available annotations are:

  • The @NewSpan annotation creates a new span, wrapping the method call or reactive type.

  • The @ContinueSpan annotation continues an existing span, wrapping the method call or reactive type.

  • The @SpanTag annotation can be used on method arguments to include the value of the argument within a Span’s tags. When you use @SpanTag on an argument, you must either annotate the method with @NewSpan or @ContinueSpan.

The following snippet presents an example of using the annotations:

Using Trace Annotations
@Singleton
class HelloService {

    @NewSpan("hello-world") (1)
    public String hello(@SpanTag("person.name") String name) { (2)
        return greet("Hello " + name);
    }

    @ContinueSpan (3)
    public String greet(@SpanTag("hello.greeting") String greet) {
        return greet;
    }
}
1 The @NewSpan annotation starts a new span
2 Use @SpanTag to include method arguments as tags for the span
3 Use the @ContinueSpan annotation to continue an existing span and incorporate additional tags using @SpanTag

Tracing Instrumentation

In addition to explicit tracing tags, Micronaut includes a number of instrumentations to ensure that the Span context is propagated between threads and across Microservice boundaries.

These instrumentations are found in the io.micronaut.tracing.instrument package and include Client Filters and Server Filters to propagate the necessary headers via HTTP.

Tracing Beans

If the Tracing annotations and existing instrumentations are not sufficient, Micronaut’s tracing integration registers a io.opentracing.Tracer bean which exposes the Open Tracing API and can be dependency-injected as needed.

Depending on the implementation you choose, there are also additional beans. For example for Zipkin brave.Tracing and brave.SpanCustomizer beans are available too.

2 Release History

For this project, you can find a list of releases (with release notes) here:

3 Tracing with Jaeger

Jaeger is a distributed tracing system developed at Uber that is more or less the reference implementation for Open Tracing.

Running Jaeger

The easiest way to get started with Jaeger is with Docker:

$ docker run -d \
  -e COLLECTOR_ZIPKIN_HTTP_PORT=9411 \
  -p 5775:5775/udp \
  -p 6831:6831/udp \
  -p 6832:6832/udp \
  -p 5778:5778 \
  -p 16686:16686 \
  -p 14268:14268 \
  -p 9411:9411 \
  jaegertracing/all-in-one:1.6

Navigate to http://localhost:16686 to access the Jaeger UI.

See Getting Started with Jaeger for more information.

Sending Traces to Jaeger

Using the CLI

If you create your project using the Micronaut CLI, supply the tracing-jaeger feature to include Jaeger tracing in your project:

$ mn create-app my-app --features tracing-jaeger

To send tracing spans to Jaeger, add the micronaut-tracing-jaeger dependency in your build:

implementation("io.micronaut.tracing:micronaut-tracing-jaeger")
<dependency>
    <groupId>io.micronaut.tracing</groupId>
    <artifactId>micronaut-tracing-jaeger</artifactId>
</dependency>

Then enable Jaeger tracing in your configuration (potentially only your production configuration):

application.yml
tracing:
  jaeger:
    enabled: true

By default, Jaeger will be configured to send traces to a locally running Jaeger agent.

Jaeger Configuration

There are many configuration options available for the Jaeger client that sends Spans to Jaeger, and they are generally exposed via the JaegerConfiguration class. Refer to the Javadoc for available options.

Below is an example of customizing JaegerConfiguration configuration:

Customizing Jaeger Configuration
tracing:
  jaeger:
    enabled: true
    sampler:
      probability: 0.5
    sender:
      agentHost: foo
      agentPort: 5775
    reporter:
      flushInterval: 2000
      maxQueueSize: 200
    codecs: W3C,B3,JAEGER

You can also optionally dependency-inject common configuration classes into JaegerConfiguration such as io.jaegertracing.Configuration.SamplerConfiguration just by defining them as beans. Likewise, a custom io.opentracing.ScopeManager can be injected into JaegerTracerFactory. See the API for JaegerConfiguration and JaegerTracerFactory for available injection points.

Filtering HTTP spans

It may be useful to exclude health-checks and other HTTP requests to your service. This can be achieved by adding a list of regular expression patterns to your configuration:

Filtering HTTP request spans
tracing:
  jaeger:
    enabled: true
  exclusions:
    - /health
    - /env/.*

4 Tracing with Zipkin

Zipkin is a distributed tracing system. It helps gather timing data to troubleshoot latency problems in microservice architectures. It manages both the collection and retrieval of this data.

Running Zipkin

The quickest way to get up and started with Zipkin is with Docker:

Running Zipkin with Docker
$ docker run -d -p 9411:9411 openzipkin/zipkin

Navigate to http://localhost:9411 to view traces.

Sending Traces to Zipkin

Using the CLI

If you create your project using the Micronaut CLI, supply the tracing-zipkin feature to include Zipkin tracing in your project:

$ mn create-app my-app --features tracing-zipkin

To send tracing spans to Zipkin, add the micronaut-tracing-zipkin dependency in your build:

implementation("io.micronaut.tracing:micronaut-tracing-zipkin")
<dependency>
    <groupId>io.micronaut.tracing</groupId>
    <artifactId>micronaut-tracing-zipkin</artifactId>
</dependency>

Then enable ZipKin tracing in your configuration (potentially only your production configuration):

application.yml
tracing:
  zipkin:
    enabled: true

Customizing the Zipkin Sender

To send spans you configure a Zipkin sender. You can configure a HttpClientSender that sends Spans asynchronously using Micronaut’s native HTTP client with the tracing.zipkin.http.url setting:

Configuring Multiple Zipkin Servers
tracing:
  zipkin:
    enabled: true
    http:
      url: http://localhost:9411

It is unlikely that sending spans to localhost will be suitable for production deployment, so you generally need to configure the location of one or more Zipkin servers for production:

Configuring Multiple Zipkin Servers
tracing:
  zipkin:
    enabled: true
    http:
      urls:
        - http://foo:9411
        - http://bar:9411
In production, setting TRACING_ZIPKIN_HTTP_URLS environment variable with a comma-separated list of URLs also works.

Alternatively, to use a different zipkin2.reporter.Sender implementation, you can define a bean of type zipkin2.reporter.Sender and it will be used instead.

Zipkin Configuration

There are many configuration options available for the Brave client that sends Spans to Zipkin, and they are generally exposed via the BraveTracerConfiguration class. Refer to the Javadoc for available options.

Below is an example of customizing Zipkin configuration:

Customizing Zipkin Configuration
tracing:
  zipkin:
    enabled: true
    traceId128Bit: true
    sampler:
      probability: 1

You can also optionally dependency-inject common configuration classes into BraveTracerConfiguration such as brave.sampler.Sampler just by defining them as beans. See the API for BraveTracerConfiguration for available injection points.

Filtering HTTP spans

It may be useful to exclude health-checks and other HTTP requests to your service. This can be achieved by adding a list of regular expression patterns to your configuration:

Filtering HTTP request spans
tracing:
  zipkin:
    enabled: true
  exclusions:
    - /health
    - /env/.*

5 Repository

You can find the source code of this project in this repository: