Expand description

Layers are composable helpers that can be “layered” on top of an existing Recorder to enhance or alter its behavior as desired, without having to change the recorder implementation itself.

As well, Stack can be used to easily compose multiple layers together and provides a convenience method for installing it as the global recorder, providing a smooth transition from working directly with installing exporters to installing stacks.

Here’s an example of a layer that filters out all metrics that start with a specific string:

// A simple layer that denies any metrics that have "stairway" or "heaven" in their name.
#[derive(Default)]
pub struct StairwayDeny<R>(pub(crate) R);

impl<R> StairwayDeny<R> {
    fn is_invalid_key(&self, key: &str) -> bool {
        key.contains("stairway") || key.contains("heaven")
    }
}

impl<R: Recorder> Recorder for StairwayDeny<R> {
    fn describe_counter(
        &self,
        key_name: KeyName,
        unit: Option<Unit>,
        description: SharedString,
    ) {
        if self.is_invalid_key(key_name.as_str()) {
            return;
        }
        self.0.describe_counter(key_name, unit, description)
    }

    fn describe_gauge(&self, key_name: KeyName, unit: Option<Unit>, description: SharedString) {
        if self.is_invalid_key(key_name.as_str()) {
            return;
        }
        self.0.describe_gauge(key_name, unit, description)
    }

    fn describe_histogram(
        &self,
        key_name: KeyName,
        unit: Option<Unit>,
        description: SharedString,
    ) {
        if self.is_invalid_key(key_name.as_str()) {
            return;
        }
        self.0.describe_histogram(key_name, unit, description)
    }

    fn register_counter(&self, key: &Key) -> Counter {
        if self.is_invalid_key(key.name()) {
            return Counter::noop();
        }
        self.0.register_counter(key)
    }

    fn register_gauge(&self, key: &Key) -> Gauge {
        if self.is_invalid_key(key.name()) {
            return Gauge::noop();
        }
        self.0.register_gauge(key)
    }

    fn register_histogram(&self, key: &Key) -> Histogram {
        if self.is_invalid_key(key.name()) {
            return Histogram::noop();
        }
        self.0.register_histogram(key)
    }
}

#[derive(Default)]
pub struct StairwayDenyLayer;

impl<R> Layer<R> for StairwayDenyLayer {
    type Output = StairwayDeny<R>;

    fn layer(&self, inner: R) -> Self::Output {
        StairwayDeny(inner)
    }
}

// Now you can construct an instance of it to use it.  The layer will be wrapped around
// our base recorder, which is a debugging recorder also supplied by `metrics_util`.
let recorder = BasicRecorder;
let layer = StairwayDenyLayer::default();
let layered = layer.layer(recorder);
metrics::set_boxed_recorder(Box::new(layered)).expect("failed to install recorder");


// Working with layers directly is a bit cumbersome, though, so let's use a `Stack`.
let stack = Stack::new(BasicRecorder);
stack.push(StairwayDenyLayer::default()).install().expect("failed to install stack");


// `Stack` makes it easy to chain layers together, as well.
let stack = Stack::new(BasicRecorder);
stack
    .push(PrefixLayer::new("app_name"))
    .push(StairwayDenyLayer::default())
    .install()
    .expect("failed to install stack");

Structs

Fans out metrics to multiple recorders.

A layer for fanning out metrics to multiple recorders.

Filters and discards metrics matching certain name patterns.

A layer for filtering and discarding metrics matching certain name patterns.

Applies a prefix to every metric key.

A layer for applying a prefix to every metric key.

Routes metrics to specific target recorders.

Routes metrics to specific target recorders.

Builder for composing layers together in a top-down/inside-out order.

Traits

Decorates an object by wrapping it within another type.