Crate mick_jaeger

source ·
Expand description

Jaeger client.

Overview

In order to use this crate, you must be familiar with the concept of a span.

A span covers a certain period of time, typically from the start of an operation to the end. In other words, you generally start a span at the beginning of a function or block, and end it at the end of the function/block.

The purpose of this crate is to let you easily record spans and send them to a Jaeger server, which will aggerate them and let you visualize them.

Each span belongs to a trace. A trace is identified by a 128 bits identifier. Jaeger lets you easily visualize all the spans belonging to the same trace, even if they come from different clients.

As an example, imagine an HTTP frontend server receiving an HTTP request. It can generate a new trace id for this request, then pass this identifier around to other external processes that process parts of this request. These external processes, being all connected to the same Jaeger server, can report spans corresponding to this request.

The easiest way to start a Jaeger server for quick experimentation is through Docker:

docker run -d --name jaeger \
  -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 14250:14250 \
  -p 9411:9411 \
  jaegertracing/all-in-one:1.20

See also the official documentation.

Usage: initialization

First and foremost, call init in order to allocate all the necessary objects.

This returns a combination of a TracesIn and TracesOut. Think of them as a sender and receiver. The TracesIn is used in order to send completed spans to the TracesOut.

Sending the traces to the server isn’t covered by this library. The TracesOut must be polled using TracesOut::next, and the data sent through UDP to the Jaeger server.

let (traces_in, mut traces_out) = mick_jaeger::init(mick_jaeger::Config {
    service_name: "demo".to_string(),
});

let udp_socket = async_std::net::UdpSocket::bind("0.0.0.0:0").await.unwrap();
udp_socket.connect("127.0.0.1:6831").await.unwrap();

async_std::task::spawn(async move {
    loop {
        let buf = traces_out.next().await;
        udp_socket.send(&buf).await.unwrap();
    }
});

If TracesOut::next isn’t called often enough, in other words if the background task is too slow, the spans sent on the TracesIn will be automatically and silently discarded. This isn’t expected to happen under normal circumstances.

Usage: spans

Use the TracesIn::span method to create spans.

The basic way to use this library is to use TracesIn::span. This creates a Span object that, when destroyed, will send a report destined to the TracesOut.

Note: As long as a Span is alive, it will not be visible on the Jaeger server. You are encouraged to create short-lived spans and long-lived trace IDs.

let _span = traces_in.span(NonZeroU128::new(43).unwrap(), "something");

// do something

// The span is reported when it is destroyed at the end of the scope.

Note: Do not name your spans _, otherwise they will be destroyed immediately!

It is possible, and encouraged, to add tags to spans.

let mut _span = traces_in.span(NonZeroU128::new(43).unwrap(), "something");
_span.add_string_tag("key", "value");

Spans can have children:

fn my_function(traces_in: &std::sync::Arc<mick_jaeger::TracesIn>) {
    let mut _span = traces_in.span(NonZeroU128::new(43).unwrap(), "foo");

    // do something

    {
        let mut _span = _span.child("bar");
        // something expensive
    }
}

If an event happens at a precise point in time rather than over time, logs can also be added.

let mut _span = traces_in.span(NonZeroU128::new(43).unwrap(), "something");
_span.log().with_string("key", "value");

Differences with other crates

While there exists other crates that let you interface with Jaeger, they are all overcomplicated according to the author of mick_jaeger. Some are lossy abstractions: by trying to be easy to use, they hide important details (such as the trace ID), which causes more confusion than it helps.

mick_jaeger tries to be simple. The fact that it doesn’t handle sending to the server removes a lot of opinionated decisions concerning networking libraries and threading.

mick_jaeger could theoretically be no_std-compatible (after a few tweaks), but can’t because at the time of writing there is no no-std-compatible library for the thrift protocol.

Structs

Functions