Skip to content

Commit

Permalink
update RELEASES.md
Browse files Browse the repository at this point in the history
  • Loading branch information
tobz committed Dec 24, 2023
1 parent 0e5874d commit 4bc6c26
Showing 1 changed file with 84 additions and 8 deletions.
92 changes: 84 additions & 8 deletions metrics/RELEASES.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ long-form description and would be too verbose for the changelog alone.

### Metric metadata

Metrics now support a limited set of metadata field, which can be added to provide for context about
the metric in terms of where it originates from as well as its verbosity.
Metrics now support collecting a limited set of metadata field, which can be provided to add context
on where a metric originates from as well as its verbosity.

In the grand vision of the `metrics` crate, where everyone uses the crate to emit metrics and then
users get those metrics in their application for free... the biggest problem is that users had no
way to actually filter out metrics they didn't want. Metric metadata aims to provide a solution to
this problem.
In the grand vision of the `metrics` crate, where library authors use it to emit metrics from their
libraries, and then downstream users get those metrics in their application for free... the biggest
problem is that users had no way to actually filter out metrics they didn't want. Metric metadata
aims to provide a solution to this problem.

A new type `Metadata<'a>` has been added to track all of this information, which includes **module
path**, a **target**, and a **level**. These fields map almost directly to
Expand Down Expand Up @@ -73,8 +73,8 @@ provided automatically, as well.

### Macros overhaul

In this release, we've reworked the macros to both simplify their implementation and to hopefully
provide a more ergonomic experience for users.
We've reworked the macros to both simplify their implementation and to hopefully provide a more
ergonomic experience for users.

At a high level, we've:

Expand Down Expand Up @@ -128,6 +128,82 @@ help with build times, even if only slightly.

[filter_layer_docs]: https://docs.rs/metrics-util/latest/metrics_util/layers/struct.FilterLayer.html

### Scoped recorders

We've added support for scoped recorders, which should allow both library authors writing exporters,
as well as downstream users of `metrics`, test their implementations far more easily.

#### Global recorder

Prior to this release, the only way to test either exporters or metrics emission was to install a
special debug/test recorder as the global recorder. This conceptually works, but quickly runs into a
few issues:

- it's not thread-safe (the global recorder is, well, global)
- it requires the recorder be _implemented_ in a thread-safe way

This meant that in order to safely do this type of testing, users would have to use something like
[`DebuggingRecorder`](https://docs.rs/metrics-util/latest/metrics_util/debugging/struct.DebuggingRecorder.html)
(which is thread-safe and _could_ be used in a per-thread way, mind you) but that they would have to
install it for every single test... which could still run into issues with destroying the collected
metrics of another concurrently running test that took the same approach.

All in all, this was a pretty poor experience that required many compromises and _still_ didn't
fully allow testing metrics in a deterministic and repeatable way.

#### Scoped recorders

Scoped recorders solve this problem by allowing the temporary overriding of what is considered the
"global" recorder on the current thread. We've added a new method,
[`metrics::with_local_recorder`](https://docs.rs/metrics/latest/metrics/fn.set_boxed_recorder.html),
that allows users to pass a reference to a recorder that is used as the "global" recorder for the
duration of a closure that the user also passes.

Here's a quick example of what the prior approach looked like, and how it's been simplified by
adding support for scoped recorders:

```rust
// This is the old approach, which mind you, still isn't thread-safe if you have concurrent tests
// doing the same thing:
let global = DebuggingRecorder::per_thread();
let snapshotter = global.snapshotter();

unsafe { metrics::clear_recorder(); }
global.install().expect("global recorder should not be installed");

increment_counter!("my_counter");

let snapshot = snapshotter.snapshot();
assert_eq!(snapshot.into_vec().len(), 1);

// Now let's use a scoped recorder instead:
let scoped = DebuggingRecorder::new();
let snapshotter = scoped.snappshotter();

metrics::with_local_recorder(&scoped, || {
increment_counter!("my_counter")
});

let snapshot = snapppshotter.snapshot();
assert_eq!(snapshot.into_vec().len(), 1);
```

There's a lot of boilerplate here, but let's look specifically at what we _don't_ have to do
anymore:

- unsafely clear the global recorder
- install as the global recorder
- transfer ownership of the recorder itself

This means that recorders can now themselves hold references to other resources, without the need to
do the common `Arc<Mutex<...>>` dance that was previously required. Given the interface of
`Recorder`, interior mutability still has to be provided somehow, although now it only requires the
use of something as straightforward as `RefCell`.

Beyond testing, this also opens up additional functionality that wasn't previously available. For
example, this approach could be used to avoid the need for using thread-safe primitives when users
don't want to pay that cost (perhaps on an embedded system).

## [0.21.1] - 2023-07-02

- No notable changes.
Expand Down

0 comments on commit 4bc6c26

Please sign in to comment.