#metrics #tracing


A crate to use tracing context as metrics labels

13 releases (7 breaking)

0.8.1 Nov 2, 2021
0.8.0 Jul 19, 2021
0.7.0 Jul 14, 2021
0.3.0 Feb 3, 2021
0.1.0-alpha.5 Nov 25, 2020

#184 in Debugging

Download history 1648/week @ 2021-08-15 1603/week @ 2021-08-22 1419/week @ 2021-08-29 1627/week @ 2021-09-05 1897/week @ 2021-09-12 1467/week @ 2021-09-19 993/week @ 2021-09-26 1896/week @ 2021-10-03 1360/week @ 2021-10-10 2056/week @ 2021-10-17 1909/week @ 2021-10-24 1752/week @ 2021-10-31 775/week @ 2021-11-07 1123/week @ 2021-11-14 578/week @ 2021-11-21 1554/week @ 2021-11-28

4,141 downloads per month

MIT license

3.5K SLoC


A crate to use tracing context as metrics labels.


Use [tracing::span!] fields as [metrics] labels.

The metrics-tracing-context crate provides tools to enable injecting the contextual data maintained via span! macro from the [tracing] crate into the metrics.


First, set up tracing and metrics crates:

# use metrics_util::DebuggingRecorder;
# use tracing_subscriber::Registry;
use metrics_util::layers::Layer;
use tracing_subscriber::layer::SubscriberExt;
use metrics_tracing_context::{MetricsLayer, TracingContextLayer};

// Prepare tracing.
# let my_subscriber = Registry::default();
let subscriber = my_subscriber.with(MetricsLayer::new());

// Prepare metrics.
# let my_recorder = DebuggingRecorder::new();
let recorder = TracingContextLayer::all().layer(my_recorder);

Then emit some metrics within spans and see the labels being injected!

# use metrics_util::{layers::Layer, DebuggingRecorder};
# use tracing_subscriber::{layer::SubscriberExt, Registry};
# use metrics_tracing_context::{MetricsLayer, TracingContextLayer};
# let mysubscriber = Registry::default();
# let subscriber = mysubscriber.with(MetricsLayer::new());
# tracing::subscriber::set_global_default(subscriber).unwrap();
# let myrecorder = DebuggingRecorder::new();
# let recorder = TracingContextLayer::all().layer(myrecorder);
# metrics::set_boxed_recorder(Box::new(recorder)).unwrap();
use tracing::{span, Level};
use metrics::counter;

let user = "ferris";
let span = span!(Level::TRACE, "login", user);
let _guard = span.enter();

counter!("login_attempts", 1, "service" => "login_service");

The code above will emit a increment for a login_attempts counter with the following labels:

  • service=login_service
  • user=ferris


The integration layer works by capturing all fields present when a span is created and storing them as an extension to the span. If a metric is emitted while a span is entered, we check that span to see if it has any fields in the extension data, and if it does, we add those fields as labels to the metric key.

There are two important behaviors to be aware of:

  • we only capture the fields present when the span is created
  • we store all fields that a span has, including the fields of its parent span(s)

Lack of dynamism

This means that if you use [Span::record][tracing::Span::record] to add fields to a span after it has been created, those fields will not be captured and added to your metric key.

Span fields and ancestry

Likewise, we capture the sum of all fields for a span and its parent span(s), meaning that if you have the following span stack:

root span        (fieldA => valueA)
 ⤷ mid-tier span (fieldB => valueB)
    ⤷ leaf span  (fieldC => valueC)

Then a metric emitted while within the leaf span would get, as labels, all three fields: A, B, and C. As well, this layer does not deduplicate the fields. If you have two instance of the same field name, both versions will be included in your metric labels. Whether or not those are deduplicated, and how they're deduplicated, is an exporter-specific implementation detail.

In addition, for performance purposes, span fields are held in pooled storage, and additionally will copy the fields of parent spans. Following the example span stack from above, the mid-tier span would hold both field A and B, while the leaf span would hold fields A, B, and C.

In practice, these extra memory consumption used by these techniques should not matter for modern systems, but may represent an unacceptable amount of memory usage on constrained systems such as embedded platforms, etc.


~89K SLoC