MCPcopy Index your code
hub / github.com/awslabs/metrique

github.com/awslabs/metrique @metrique-writer-cloudwatch-v0.2.0

Chat with this repo
repository ↗ · DeepWiki ↗ · release metrique-writer-cloudwatch-v0.2.0 ↗ · + Follow
2,507 symbols 7,087 edges 201 files 535 documented · 21% updated 3d agometrique-v0.1.26 · 2026-06-09★ 7057 open issues

Browse by type

Functions 1,687 Types & classes 820
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

Metrique Build Status Latest Version Released API docs Apache-2.0 licensed

Metrique is a set of crates for collecting and exporting wide events: structured metric records that capture everything about a single action.

use metrique::unit_of_work::metrics;

#[metrics]
struct RequestMetrics {
    #[metrics(timestamp)]
    timestamp: Timestamp,
    number_of_ducks: usize,
    #[metrics(unit = Millisecond)]
    operation_time: Timer,
}

This currently supports exporting metrics in Amazon EMF format to CloudWatch and as plain JSON for non-AWS systems. Metrics can be printed locally with [metrique::local::LocalFormat]. Formats can be implemented outside of this crate via the Format trait.

Why Metrique?

Metrique is designed for high-performance, structured metrics collection with minimal runtime overhead. Metrique is built around the principle that a metric associated with a specific action is more valuable than those that are only available aggregated over time. We call these wide events: structured records that capture all the metrics, dimensions, and context for a single action. The most common type of wide event is a unit-of-work metric, where each record corresponds to a single unit of application work (an API request, a background job, a queue item).

Performance

Unlike metrics libraries that collect metrics in a HashMap, metrique uses plain structs. This eliminates allocation and hashmap lookups when producing metrics, resulting in significantly lower CPU overhead and memory pressure. This is especially important for high-throughput services.

Compared to libraries that rely on HashMaps or similar containers, the overhead of metrique can be 50x lower!

Structured Metrics with Type Safety

Because metrique builds on plain structs, metric structure is enforced at compile time. Your metrics are defined as structs with the #[metrics] attribute, ensuring consistency and catching errors early rather than at runtime. Structuring your metrics up front has some up front cost but it pays for itself in the long term.

Minimal Allocation Overhead

metrique-writer, the serialization library for metrique, enables low (and sometimes 0) allocation formatting for EMF. Coupled with the fact that metrics-are-just-structs, this can significantly reduce allocator pressure.

Why use metrique?

Instead of OpenTelemetry

OTel and metrique solve different problems. In OTel terms, a metrique record is closest to a wide event (a richly attributed log or span) rather than an OTel metric. metrique is about emitting wide events that capture all the metrics associated with a single action, in Rust, as efficiently as possible. Future work may add OTel backends for both wide event export and OTel's aggregated metric observation style.

Instead of metrics.rs

metrique is actually compatible with metrics.rs via the [metrique-metricsrs] crate! This allows you to periodically flush the contents of metrics collected via libraries already compatible with metrics.rs as a single event.

However, if your goal is to emit structured events that produce metrics with as little overhead as possible: - Metrique avoids HashMap-based metric storage, reducing allocation pressure and the overhead of recording metrics - Compile-time metric definition prevents typos and makes it obvious exactly what metrics your application produces

Getting Started

Most applications and libraries will use [metrique] directly and configure a writer with [metrique-writer]. See the examples for several examples of different common patterns.

Applications will define a metrics entry struct that they annotate with #[metrics]:

use metrique::unit_of_work::metrics;
use metrique::timers::{Timestamp, Timer};

// Enums containing fields are also supported
#[metrics(value(string))]
enum Operation {
    CountDucks,
}

#[metrics(rename_all = "PascalCase")]
struct RequestMetrics {
    operation: Operation, // you can use `operation: &'static str` if you prefer
    #[metrics(timestamp)]
    timestamp: Timestamp,
    number_of_ducks: usize,
    #[metrics(unit = Millisecond)]
    operation_time: Timer,
    success: bool // flushes as 0 or 1
}

On its own, this is just a normal struct, there is no magic. To use it as a metric, you can call .append_on_drop:

impl RequestMetrics {
    // It is generally a good practice to expose a single initializer that sets up
    // append on drop.
    fn init(operation: Operation) -> RequestMetricsGuard {
        RequestMetrics {
            timestamp: Timestamp::now(),
            operation,
            number_of_ducks: 0,
            operation_time: Timer::start_now(),
            success: false,
        }.append_on_drop(ServiceMetrics::sink())
    }
}

The guard object can still be mutated via DerefMut impl:

async fn count_ducks() {
    let mut metrics = RequestMetrics::init(Operation::CountDucks);
    metrics.number_of_ducks = 5;
    metrics.success = true;
    // metrics flushes as scope drops
    // timer records the total time until scope exits
}

But when it drops, it will be appended to the queue to be formatted and flushed.

To control how it is written, when you start your application, you must configure a queue:

pub use metrique::ServiceMetrics;

fn initialize_metrics(service_log_dir: PathBuf) -> AttachHandle {
    ServiceMetrics::attach_to_stream(
        Emf::builder("Ns".to_string(), vec![vec![]])
            .build()
            .output_to_makewriter(RollingFileAppender::new(
                Rotation::MINUTELY,
                &service_log_dir,
                "service_log.log",
            )),
    )
}

See [metrique-writer] for more information about queues and destinations.

You can either attach it to a global destination or thread the queue to the location you construct your metrics object directly.

For production, only formatters for Amazon EMF and plain JSON ([metrique-writer-format-json]) are provided, but more may be added in the future.

For local development, [metrique::local::LocalFormat] provides human-readable output (pretty-printed key-value pairs, JSON, or markdown tables) with automatic histogram percentile computation. See the module docs for a guide on implementing your own custom format.

You can also implement a custom format using the [Format] trait. If you do, you can optionally implement a custom [EntrySink] if you need flush functionality beyond writing bytes to an arbitrary I/O destination.

Aggregation

When you have many observations of the same metric within a single wide event, you can use histograms to aggregate them into a distribution rather than emitting each observation individually.

The [metrique-aggregation] crate provides histogram types that collect observations and emit them as distributions:

use metrique::unit_of_work::metrics;
use metrique_aggregation::histogram::{Histogram, ExponentialAggregationStrategy};
use metrique_writer::unit::Millisecond;
use std::time::Duration;

#[metrics(rename_all = "PascalCase")]
struct QueryMetrics {
    query_id: String,

    #[metrics(unit = Millisecond)]
    backend_latency: Histogram<Duration, ExponentialAggregationStrategy>,
}

Common use cases include: - Distributed queries that fan out to multiple backend services - Batch processing where you want to track per-item latency - Any operation that generates multiple measurements to aggregate

For most applications, sampling is a better approach than aggregation. Consider histograms when you need precise distributions for high-frequency events.

Glossary

  • dimension: The keys for metrics are generally of the form (name, dimensions). Metric backends have ways of aggregating metrics according to some sets of dimensions.

For example, a metric named RequestCount can be emitted with dimensions [(Status, <http status>), (Operation, <operation>)]. Then, the metric backend could allow for counting the requests with status 500 for operation Frobnicate. - entry io stream: An object that implements [EntryIoStream] - should be wrapped into an [EntrySink] before use - see the [EntryIoStream] docs for more details. - entry sink: An object that implements [EntrySink], that normally writes entries as metric records to some entry destination outside the program. Normally a [BackgroundQueue] or a [FlushImmediately]. - guard: a Rust object that performs some action on drop. In a metrique context, normally an [AppendAndCloseOnDrop] that emits a metric entry when dropped. - metric: A metric is a (name, dimensions) key that can have values associated with it. Generally, a metric contains metric datapoints. - metric backend: The backend being used to aggregate metrics. metrique currently comes with support for Amazon EMF and plain JSON backends, and support can be added for other backends. - metric datapoint: A single point of (name, dimensions, multiplicity, time, value), generally not represented explicitly but rather being emitted from fields in a metric entry. Metric datapoints have a value that is an integer or floating point, and can come with some sort of multiplicity. - metric entry: something that implements [Entry] (when using metrique rather than using metrique-writer directly, this will be a [RootEntry] wrapping an [InflectableEntry]). Will create a metric record (e.g., an EMF JSON entry) when emitted. - metric record: the data recorded created from emitting a metric entry and sent to the metric backend. Will create metric datapoints for the included metrics - multiplicity: Is a property of a metric value, that allows it to count as a large number of datapoints with O(1) emission complexity. metrique allows users to emit metric datapoint with multiplicity. - property: In addition to metric datapoints, metric entries can also contain string-valued properties, that are normally not automatically aggregated directly by the metric backend, but can be used as keys for aggregations - for example, it is sometimes useful to include the host machine and software version as properties. - slot: A [Slot], which can be used in metrique to write to a part of a metric entry from a different task or thread. A [Slot] can also hold a reference to a [FlushGuard] that can delay metric entry emission until the [Slot] is finalized.

Security

See CONTRIBUTING for more information.

License

This project is licensed under the Apache-2.0 License.

Extension points exported contracts — how you extend this code

EntrySink (Interface)
Stores entries in an in-memory buffer until they can be written to the destination. Implementations of this trait norma [8 …
metrique-writer-core/src/sink.rs
AggregateValue (Interface)
Defines how individual field values are aggregated. This trait operates at the field level, not the entry level. Each a [8 …
metrique-aggregation/src/traits.rs
InflectableEntry (Interface)
A trait for metric entries where the names of the fields can be "inflected" using a [`NameStyle`]. This defines the inte [10 …
metrique-core/src/lib.rs
CwLogsStreamObserver (Interface)
Observer for [`CwLogsStreamEvent`]s. Implement this to collect internal metrics. [3 implementers]
metrique-writer-cloudwatch/src/lib.rs
Time (Interface)
Trait for providing custom time sources Implementors of this trait can be used to provide custom time behavior for test [3 …
metrique-timesource/src/lib.rs
FlushImmediatelyObserver (Interface)
Receives lifecycle events from a [`FlushImmediately`] sink. Plug an implementation into [`FlushImmediatelyBuilder::obse [3 …
metrique-writer/src/sink/observer.rs
MetricsRsVersion (Interface)
A trait to allow the metrics.rs bridge to be generic over metrics.rs versions This is not to be implemented or called d [2 …
metrique-metricsrs/src/generic.rs
AttachGlobalEntrySinkTokioMetricsExt (Interface)
Extension methods for subscribing Tokio runtime metrics to a global entry sink. Spawns a background task that periodica [1 …
metrique-util/src/tokio_metrics_reporter.rs

Core symbols most depended-on inside this repo

Shape

Method 847
Function 840
Class 633
Enum 115
Interface 72

Languages

Rust100%
Python1%

Modules by API surface

metrique-writer-format-emf/src/emf.rs103 symbols
metrique-macro/src/lib.rs78 symbols
metrique-writer-format-json/src/json.rs68 symbols
metrique-writer-cloudwatch/src/lib.rs59 symbols
metrique-writer/src/sink/background.rs55 symbols
metrique-writer-core/src/global.rs50 symbols
metrique-timesource/src/lib.rs49 symbols
metrique/src/local.rs48 symbols
metrique/src/timers.rs46 symbols
metrique-writer/src/test_util.rs46 symbols
metrique-writer/src/sample/congress.rs37 symbols
metrique/tests/kitchen-sink.rs34 symbols

For agents

$ claude mcp add metrique \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact

Ask about this repo answers extend the page