Crate metrics_tracing_context[][src]

Expand description

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_util::layers::Layer;
use tracing_subscriber::layer::SubscriberExt;
use metrics_tracing_context::{MetricsLayer, TracingContextLayer};

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

Re-exports

pub use label_filter::LabelFilter;

Modules

Label filtering.

Structs

MetricsLayer is a tracing_subscriber::Layer that captures the span fields and allows them to be later on used as metrics labels.

TracingContext is a metrics::Recorder that injects labels from tracing::Spans.

TracingContextLayer provides an implementation of a Layer for TracingContext.