#metrics #tracing

metrics-tracing-context

A crate to use tracing context as metrics labels

19 releases (breaking)

0.14.0 Apr 16, 2023
0.13.0 Jan 21, 2023
0.12.0 Jul 21, 2022
0.10.0 Jan 15, 2022
0.1.0-alpha.5 Nov 25, 2020

#413 in Debugging

Download history 6233/week @ 2023-08-12 9025/week @ 2023-08-19 8168/week @ 2023-08-26 6488/week @ 2023-09-02 11659/week @ 2023-09-09 9072/week @ 2023-09-16 7677/week @ 2023-09-23 8977/week @ 2023-09-30 5348/week @ 2023-10-07 5878/week @ 2023-10-14 6369/week @ 2023-10-21 8348/week @ 2023-10-28 8789/week @ 2023-11-04 6355/week @ 2023-11-11 4778/week @ 2023-11-18 6194/week @ 2023-11-25

27,138 downloads per month

MIT license

275KB
4.5K SLoC

metrics-tracing-context

A crate to use tracing context as metrics labels.


lib.rs:

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.

Usage

First, set up tracing and metrics crates:

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

// Prepare tracing.
let subscriber = my_subscriber.with(MetricsLayer::new());
tracing::subscriber::set_global_default(subscriber).unwrap();

// Prepare metrics.
let recorder = TracingContextLayer::all().layer(my_recorder);
metrics::set_boxed_recorder(Box::new(recorder)).unwrap();

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

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

Implementation

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

Dependencies

~5MB
~49K SLoC