#jaeger #opentelemetry #server #client


Rust client for sending traces to a Jaeger server

8 releases

0.1.8 Mar 10, 2022
0.1.7 Dec 15, 2021
0.1.6 Nov 9, 2021
0.1.4 Jan 6, 2021
0.1.1 Nov 8, 2020
Download history 5776/week @ 2022-10-25 6926/week @ 2022-11-01 8311/week @ 2022-11-08 6709/week @ 2022-11-15 6972/week @ 2022-11-22 7544/week @ 2022-11-29 7992/week @ 2022-12-06 6788/week @ 2022-12-13 6718/week @ 2022-12-20 3823/week @ 2022-12-27 6880/week @ 2023-01-03 7468/week @ 2023-01-10 8457/week @ 2023-01-17 7432/week @ 2023-01-24 6770/week @ 2023-01-31 6997/week @ 2023-02-07

31,293 downloads per month


2.5K SLoC


Rust client for sending traces to a Jaeger server.

Everything is documented at the crate root. See docs.rs.


Jaeger client.


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 \
  -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 \

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.

# async fn foo() {
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("").await.unwrap();

async_std::task::spawn(async move {
    loop {
        let buf = traces_out.next().await;
# }

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.

# use std::num::NonZeroU128;
# let mut traces_in: std::sync::Arc<mick_jaeger::TracesIn> = return;
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.

# use std::num::NonZeroU128;
# let mut traces_in: std::sync::Arc<mick_jaeger::TracesIn> = return;
let mut _span = traces_in.span(NonZeroU128::new(43).unwrap(), "something");
_span.add_string_tag("key", "value");

Spans can have children:

# use std::num::NonZeroU128;
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.

# use std::num::NonZeroU128;
# let mut traces_in: std::sync::Arc<mick_jaeger::TracesIn> = return;
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.


~37K SLoC