PackageTrack
Sign in Get early access

metrics

A lightweight metrics facade.

0.24.6 101M downloads/mo #802 most downloaded on crates.io metrics-rs/metrics

What this package is like to depend on

Last release 3 months ago

13 May 2026

Ships fairly regularly

a new release about every 3 months

Some releases are documented

notes for 16 of 43 stable releases

4 versions withdrawn

withdrawn after publishing

11 years old

59 releases · first in 2015

4 releases in the last 12 months

see the full history below

Release timeline

59 releases · Sep 2015 to May 2026
2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026
Release Pre-release Withdrawn

Releases

latest 59
  1. 0.24.6 13 May 2026
    Release notes

    chore: Release metrics version 0.24.6

    Open source →
    Release notes
    • No notable changes.
    Open source →
  2. 0.24.5 29 Apr 2026 withdrawn
    Release notes

    chore: Release metrics version 0.24.5

    Open source →
    Release notes
    • No notable changes.
    Open source →
  3. 0.24.4 28 Apr 2026 withdrawn
    Release notes

    chore: Release metrics version 0.24.4

    Open source →
    Release notes
    • No notable changes.
    Open source →
  4. 0.24.3 28 Nov 2025
    Release notes
    • No notable changes.
    Open source →
  5. 0.24.2 20 Apr 2025
    Release notes
    • No notable changes.
    Open source →
  6. 0.24.1 26 Nov 2024
    Release notes
    • No notable changes.
    Open source →
  7. 0.24.0 12 Oct 2024
    Release notes
    • No notable changes.
    Open source →
  8. 0.23.1 16 Apr 2025

    Nothing published for this version

  9. 0.23.0 27 May 2024
    Release notes
    • No notable changes.
    Open source →
  10. 0.22.4 16 Apr 2025

    Nothing published for this version

  11. 0.22.3 16 Mar 2024
    Release notes
    • No notable changes.
    Open source →
  12. 0.22.2 16 Mar 2024
    Release notes
    • No notable changes.
    Open source →
  13. 0.22.1 11 Feb 2024
    Release notes
    • No notable changes.
    Open source →
  14. 0.22.0 24 Dec 2023
    Release notes

    Metric metadata

    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 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 tracing -- the inspiration for adding this metadata support -- and provide the ability to:

    • group/filter metrics by where they're defined (module path)
    • group/filter metrics by where they're emitted from (target)
    • group/filter metrics by their verbosity (level)

    Metadata<'a> is passed into the Recorder API when registering a metric so that exporters can capture it and utilize it.

    Examples

    As an example, users may wish to filter out metrics defined by a particular crate because they don't care about them at all. While they might have previously been able to get lucky and simply filter the metrics by a common prefix, this still allows for changes to the metric names to break the filter configuration. If we instead filtered by module path, where we can simply use the crate name itself, then we'd catch all metrics for that crate regardless of their name and regardless of the crate version.

    Similarly, as another example, users may wish to only emit common metrics related to operation of their application/service in order to consume less resources, pay less money for the ingest/storage of the metrics, and so on. During an outage, or when debugging an issue, though, they may wish to increase the verbosity of metrics they emit in order to capture more granular detail. Being able to filter by level now provides a mechanism to do so.

    Usage

    First, it needs to be said that nothing in the core metrics crates actually utilizes this metadata yet. We'll add support in the future to existing layers, such as the filter layer, in order to take advantage of this support.

    With that said, actually setting this metadata is very easy! As a refresher, you'd normally emit metrics with something like this:

    metrics::increment_counter!("my_counter");
    

    Now, you can specify the additional metadata attributes as fields at the beginning of the macro call. This applies to all of the "emission" macros for counters, gauges, and histograms:

    metrics::increment_counter!(target: "myapp", "my_counter");
    
    metrics::increment_gauge!(level: metrics::Level::DEBUG, "my_gauge", 42.2);
    
    metrics::histogram!(target: "myapp", level: metrics::Level::DEBUG, "my_histogram", 180.1);
    

    These metrics will have the relevant metadata field set, and all of them will get the module path provided automatically, as well.

    Macros overhaul

    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:

    • removed all the various macros that were tied to specific operations (e.g. increment_counter! for incrementing a counter) and replaced them with one macro per metric type
    • removed the standalone registration macros (e.g. register_counter!)
    • exposed the operations as methods on the metric handles themselves
    • switched from using procedural macros to declarative macros

    Fewer macros, more ergonomic usage

    Users no longer need to remember the specific macro to use for a given metric operation, such as increment_gauge! or decrement_gauge!. Instead, if the user knows they're working with a gauge, they simply call gauge!(...) to get the handle, and chain on a method call to perform the operation, such as gauge!(...).increment(42.2).

    Additionally, because we've condensed the registration macros into the new, simplified macros, the same macro is used whether registering the metric to get a handle, or simply performing an operation on the metric all at once.

    Let's look at a few examples:

    // This is the old style of registering a metric and then performing an operation on it.
    //
    // We're working with a counter here.
    let counter_handle = metrics::register_counter!("my_counter");
    counter_handle.increment(1);
    
    metrics::increment_counter!("my_counter");
    
    // Now let's use the new, simplified macros instead:
    let counter_handle = metrics::counter!("my_counter");
    counter_handle.increment(1);
    
    metrics::counter!("my_counter").increment(1);
    

    As you can see, users no longer need to know about as many macros, and their usage is consistent whether working with a metric handle that is held long-term, or chaining the method call inline with the macro call. As a benefit, this also means that IDE completion will be better in some situations, as support for autocompletion of method calls is generally well supported, while macro autocompletion is effectively nonexistent.

    Declarative macros

    As part of this rework, the macros have also been rewritten declaratively. While the macro code itself is slightly more complicated/verbose, it has a major benefit that the metrics-macros crate was able to be removed. This is one less dependency that has to be compiled, which should hopefully help with build times, even if only slightly.

    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 (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, 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:

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

    Open source →
  15. 0.21.1 02 Jul 2023
    Release notes
    • No notable changes.
    Open source →
  16. 0.21.0 16 Apr 2023
    Release notes
    • No notable changes.
    Open source →
  17. 0.20.1 22 Jul 2022
    Release notes
    • No notable changes.
    Open source →
  18. 0.20.0 21 Jul 2022 withdrawn
    Release notes
    • No notable changes.
    Open source →
  19. 0.19.0 30 May 2022
    Release notes
    • No notable changes.
    Open source →
  20. 0.18.1 11 Mar 2022
    Release notes
    • No notable changes.
    Open source →
  21. 0.18.0 15 Jan 2022
    Release notes

    Switch to metric handles

    metrics has now switched to "metric handles." This requires a small amount of backstory.

    Evolution of data storage in metrics

    Originally, metrics started its life as metrics-runtime, which represented a batteries-included implementation of metrics storage, with both a user-accessible API that allowed directly interacting with the metrics as well as a mode for being installed as the global recorder for [metrics].

    The user-accessible API was most commonly accessed via a helper type called Sink, which could both create metric type-specific handles -- such as Counter or Gauge -- and could also perform operations directly, such as increment_counter.

    However, users still needed to both create the machinery supplied by metrics-runtime to handle metrics storage, as well as the actual exporters to access those metrics, or send them to the downstream system of choice. We ultimately made the decision to bundle the metrics storage capabilities into exporters directly by moving much of metrics-runtime to metrics-util as a set of reusable components.

    In doing so, though, we lost the ability to access metrics directly, as the metrics facade became the only way to register or emit metrics, and this could only happen through the opaque macros, going through the opaque Recorder trait, and so on. This made it easy for all users to simply interact with the macros, but made it harder to optimize the performance of recording metrics.

    With this update, we've switched back to a handle-based model that also works seamlessly with the macros.

    Handle-based design

    The metrics crate now provides concrete handle types for all metrics. The Recorder API has also been updated to return these handles when registering a metric. Thus, for macros, instead of simply calling the operation directly on the global recorder, they now register the metric first and call the operation on the handle. If the metric has already been registered, similar to as before, a copy of the handle is simply returned and can be operated as normally.

    On the flipside, though, users can use directly acquire these handles using the register_* macros. The macros for adding descriptive information have been renamed to describe_*. Thus, when there is an opportunity to register metrics and store their handles somewhere for later reuse, users can use the register_* macros to do so.

    Implementation of handles

    In order to provide an mechanism that allows enough flexibility for exporter authors, the design of the handle types was considered in depth.

    Simply put, handles represent an incredibly thin layer over compatible implementations of each metric type. There are now three traits, one for each metric type, that must be implemented to satisfy being wrapped in the corresponding handle type. Internally, this is wrapped in an Arc<T> to make it usable in multi-threaded scenarios.

    Implementations of Counter and Gauge based on atomic integers can be found in metrics, and an example of Histogram can be found in metrics-util.

    Limitations of handles

    In most cases, the new handle-based design will be effectively invisible, with no visible changes to behavior. There is one scenario where changes could be encountered, though: use of layers.

    Layers work on a per-metric basis, which means that when a handle is acquired and used directly, any layers that were used no longer have a way to intercept the calls made to handles. When the macros are used, however, a call is always made to "register" the metric, which provides a chance for the layers to intercept and modify things as necessary.

    As a concrete example, the metrics-tracing-context layer exists to annotate a metric's set of labels with fields from the tracing::Span that is currently entered when the metric is updated. Naturally, a given metric could be emitted while in a various number of different spans, but if a metric is first registered in a particular span, and then the handle is used from that point on... only the original span fields will be attached to the metric.

    This is an inherent trade-off between providing a way to reduce the overhead of updating a metric to the bare minimum, and providing extensibility via layers.

    Open source →
  22. 0.17.1 16 Dec 2021

    Nothing published for this version

  23. 0.17.0 13 Jul 2021

    Nothing published for this version

  24. 0.16.0 18 May 2021

    Nothing published for this version

  25. 0.15.1 03 May 2021

    Nothing published for this version

  26. 0.15.0 03 May 2021 withdrawn

    Nothing published for this version

  27. 0.14.2 13 Feb 2021

    Nothing published for this version

  28. 0.14.1 03 Feb 2021

    Nothing published for this version

  29. 0.14.0 03 Feb 2021

    Nothing published for this version

  30. 0.13.1 24 Jan 2021

    Nothing published for this version

  31. 0.13.0 22 Jan 2021

    Nothing published for this version

  32. 0.13.0-alpha.13 18 Dec 2020 pre-release

    Nothing published for this version

  33. 0.13.0-alpha.11 25 Nov 2020 pre-release

    Nothing published for this version

  34. 0.13.0-alpha.10 25 Nov 2020 pre-release

    Nothing published for this version

  35. 0.13.0-alpha.9 25 Nov 2020 pre-release

    Nothing published for this version

  36. 0.13.0-alpha.8 24 Oct 2020 pre-release

    Nothing published for this version

  37. 0.13.0-alpha.7 24 Oct 2020 pre-release

    Nothing published for this version

  38. 0.13.0-alpha.6 24 Oct 2020 pre-release

    Nothing published for this version

  39. 0.13.0-alpha.5 04 Oct 2020 pre-release

    Nothing published for this version

  40. 0.13.0-alpha.4 16 Sep 2020 pre-release

    Nothing published for this version

  41. 0.13.0-alpha.3 27 Jul 2020 pre-release

    Nothing published for this version

  42. 0.13.0-alpha.2 22 Jul 2020 pre-release

    Nothing published for this version

  43. 0.13.0-alpha.1 17 Jun 2020 pre-release

    Nothing published for this version

  44. 0.12.1 21 Nov 2019

    Nothing published for this version

  45. 0.12.0 18 Oct 2019

    Nothing published for this version

  46. 0.11.1 09 Aug 2019

    Nothing published for this version

  47. 0.11.0 30 Jul 2019

    Nothing published for this version

  48. 0.10.3 12 Jun 2019

    Nothing published for this version

  49. 0.10.2 12 Jun 2019

    Nothing published for this version

  50. 0.10.1 12 Jun 2019

    Nothing published for this version

  51. 0.10.0 11 Jun 2019

    Nothing published for this version

  52. 0.9.1 01 May 2019

    Nothing published for this version

  53. 0.9.0 24 Apr 2019

    Nothing published for this version

  54. 0.2.3 25 May 2017

    Nothing published for this version

  55. 0.2.2 09 Dec 2016

    Nothing published for this version

  56. 0.2.0 03 Sep 2016

    Nothing published for this version

  57. 0.1.1 27 May 2016

    Nothing published for this version

  58. 0.1.0 27 May 2016

    Nothing published for this version

  59. 0.0.2 03 Sep 2015

    Nothing published for this version

Every package, every release, already written down.

The archive is open and free. Watching your own project is what we are building next.

Browse the archive