### Storage Operation Metrics Example in Rust Source: https://github.com/awslabs/metrique/blob/main/docs/aggregated.md Presents a Rust example for `StorageOp` metrics using `#[metrics(aggregate)]`. This metric set includes key identifiers like `operation_type` and `storage_tier`, along with aggregated fields for operation counts, latency histograms, and byte processing counters. ```rust use metrique::writer::merge::{Counter, Histogram}; use std::time::Duration; // Assuming Byte unit is defined elsewhere or part of metrique // use metrique::unit::Byte; #[metrics(aggregate)] struct StorageOp { #[metrics(key)] operation_type: &'static str, // "read", "write", "delete" #[metrics(key)] storage_tier: &'static str, // "hot", "warm", "cold" #[metrics(aggregate = Counter)] operations_count: u64, #[metrics(aggregate = Histogram)] operation_latency: Duration, #[metrics(aggregate = Counter, unit = Byte)] // Assuming Byte unit is correctly specified bytes_processed: u64, } ``` -------------------------------- ### Timing Events with Metrique Timing Primitives Source: https://github.com/awslabs/metrique/blob/main/metrique/README.md This Rust example showcases various timing primitives provided by the `metrique` crate for measuring time intervals and timestamps. It includes `Timestamp`, `TimestampOnClose`, `Timer`, `Stopwatch`, and `Duration`, illustrating their usage with the `#[metrics]` macro for different event timing scenarios. The example also shows how to configure timestamp formatting. ```rust use metrique::timers::{Timestamp, TimestampOnClose, Timer, Stopwatch}; use metrique::unit::Millisecond; use metrique::timers::EpochSeconds; use metrique::unit_of_work::metrics; use std::time::Duration; #[metrics] struct TimerExample { // record a timestamp when the record is created (the name // of the field doesn't affect the generated metrics) // // If you don't provide a timestamp, most formats will use the // timestamp of when your record is formatted (read your // formatter's docs for the exact details). // // Multiple `#[metrics(timestamp)]` will cause a validation error, so // normally only the top-level metric should have a // `#[metrics(timestamp)]` field. #[metrics(timestamp)] timestamp: Timestamp, // some other timestamp - not emitted if `None` since it's optional. // // formatted as seconds from epoch. #[metrics(format = EpochSeconds)] some_other_timestamp: Option, // records the total time the record is open for time: Timer, // manually record the duration of a specific event subevent: Stopwatch, // typically, you won't have durations directly since you'll use // timing primitives instead. However, note that `Duration` works // just fine as a metric type: #[metrics(unit = Millisecond)] manual_duration: Duration, #[metrics(format = EpochSeconds)] end_timestamp: TimestampOnClose, } ``` -------------------------------- ### TLS Handshake Metrics Example in Rust Source: https://github.com/awslabs/metrique/blob/main/docs/aggregated.md Provides a Rust code example defining `TlsHandshake` metrics using the `#[metrics(aggregate)]` proc macro. It includes key fields like `cipher_suite` and `tls_version`, and aggregated fields for counters and histograms, such as `handshakes_completed` and `handshake_duration`. ```rust use metrique::writer::merge::{Counter, Histogram}; use std::time::Duration; #[metrics(aggregate)] struct TlsHandshake { #[metrics(key)] cipher_suite: &'static str, #[metrics(key)] tls_version: &'static str, #[metrics(aggregate = Counter)] handshakes_completed: u64, #[metrics(aggregate = Histogram)] handshake_duration: Duration, #[metrics(aggregate = Counter)] handshake_failures: u64, } ``` -------------------------------- ### Configure Background Queue Metric Sink Source: https://github.com/awslabs/metrique/blob/main/metrique/README.md Sets up a metric sink using a `BackgroundQueue` that buffers entries in memory and writes them to an output stream in a background thread. This example configures the sink to output EMF formatted metrics to standard output. ```rust use metrique::emf::Emf; use metrique::ServiceMetrics; use metrique::writer::{AttachGlobalEntrySinkExt, FormatExt, GlobalEntrySink}; let handle = ServiceMetrics::attach_to_stream( Emf::builder("Ns".to_string(), vec![vec![]]) .build() .output_to(std::io::stdout()) ); # use metrique::unit_of_work::metrics; # #[metrics] # struct MyEntry {} # MyEntry {}.append_on_drop(ServiceMetrics::sink()); ``` -------------------------------- ### Create Typed Non-Global Sink in Rust Source: https://github.com/awslabs/metrique/blob/main/metrique/README.md This example shows how to create a sink that is specific to an entry type, like `RootMetric`. This approach avoids virtual dispatch and can improve performance in high-throughput scenarios compared to global sinks. The sink is implemented using `BackgroundQueue`. ```rust use metrique::{CloseValue, RootMetric}; use metrique::emf::Emf; use metrique::writer::{EntrySink, FormatExt}; use metrique::writer::sink::BackgroundQueue; use metrique::unit_of_work::metrics; #[metrics] #[derive(Default)] struct MyEntry { value: u32 } type MyRootEntry = RootMetric; let (queue, handle) = BackgroundQueue::::new( Emf::builder("Ns".to_string(), vec![vec![]]) .build() .output_to(std::io::stdout()) ); handle_request(&queue); fn handle_request(queue: &BackgroundQueue) { let mut metric = MyEntry::default(); metric.value += 1; // or you can `metric.append_on_drop(queue.clone())`, but that clones an `Arc` // which has slightly negative performance impact queue.append(MyRootEntry::new(metric.close())); } ``` -------------------------------- ### Initialize MetricReporter in Rust using metrique-metricsrs Source: https://github.com/awslabs/metrique/blob/main/metrique-metricsrs/README.md This Rust code demonstrates how to initialize and install the `MetricReporter` from the `metrique-metricsrs` crate. It specifies the `metrics.rs` version and configures an output stream using `metrique-writer` with rolling file appender for logging metrics. ```rust # use metrics_024 as metrics; use metrique_metricsrs::MetricReporter; use metrique_writer::{Entry, EntryIoStream, FormatExt, EntryIoStreamExt}; use metrique_writer_format_emf::Emf; use tracing_appender::rolling::{RollingFileAppender, Rotation}; let log_dir = std::path::PathBuf::from("example"); let logger = MetricReporter::builder() .metrics_rs_version::() .metrics_io_stream(Emf::all_validations("MyNS".to_string(), vec![vec![], vec!["service".to_string()]] ).output_to_makewriter( RollingFileAppender::new(Rotation::HOURLY, &log_dir, "metric_log.log") ) ) .build_and_install(); ``` -------------------------------- ### Implement Custom Timer in Rust Source: https://github.com/awslabs/metrique/blob/main/metrique/README.md Shows how to implement custom behavior for metrics closing by implementing the `CloseValue` and `CloseValueRef` traits in Rust. This example creates a custom timer that calculates elapsed time from instantiation to closure. ```rust use metrique::{CloseValue, CloseValueRef}; use std::time::{Duration, Instant}; struct MyTimer(Instant); impl Default for MyTimer { fn default() -> Self { Self(Instant::now()) } } // this does not take ownership, and therefore should implement `CloseValue` for both &T and T impl CloseValue for &'_ MyTimer { type Closed = Duration; fn close(self) -> Self::Closed { self.0.elapsed() } } impl CloseValue for MyTimer { type Closed = Duration; fn close(self) -> Self::Closed { self.close_ref() /* this proxies to the by-ref implementation */ } } ``` -------------------------------- ### Setting Units for Metrics in Rust Source: https://github.com/awslabs/metrique/blob/main/metrique/README.md Illustrates how to define custom units for metrics using the `metrique::unit` module. This example shows how to apply a `Megabyte` unit to a `request_size` field within a `RequestMetrics` struct using the `#[metrics(unit = ...)]` attribute. This ensures the unit is included in the metric's output. ```rust use metrique::unit_of_work::metrics; use metrique::unit::Megabyte; #[metrics(rename_all = "PascalCase")] struct RequestMetrics { operation: &'static str, #[metrics(unit = Megabyte)] request_size: usize } ``` -------------------------------- ### JSON: Example Metrique Output Source: https://github.com/awslabs/metrique/blob/main/metrique/README.md An example of the JSON output generated by Metrique when capturing application metrics. This format is compatible with AWS CloudWatch Metrics, including namespace, dimensions, metric names, units, and timestamp. ```json {"_aws":{"CloudWatchMetrics":[{"Namespace":"Ns","Dimensions":[[]],"Metrics":[{"Name":"NumberOfDucks"},{"Name":"OperationTime","Unit":"Milliseconds"}]}]},"Timestamp":1752774958378,"NumberOfDucks":5,"OperationTime":0.003024,"Operation":"CountDucks"} ``` -------------------------------- ### Create Boxed BackgroundQueue for Multiple Metric Types (Rust) Source: https://github.com/awslabs/metrique/blob/main/metrique-writer/README.md This example demonstrates creating a `BackgroundQueue` that can accept multiple metric types using `BoxEntry`. It uses `BackgroundQueueBuilder` for more flexible configuration, including merging global dimensions and outputting to a rolling file. Metrics are explicitly appended to the queue. ```rust use metrique_writer::{ Entry, BoxEntry, AnyEntrySink, format::FormatExt as _, sink::BackgroundQueueBuilder, unit::AsCount, }; use metrique_writer_format_emf::Emf; use tracing_appender::rolling::{RollingFileAppender, Rotation}; #[derive(Entry, Default)] struct MyMetrics { field: AsCount, } #[derive(Entry)] struct Globals { region: String, } let log_dir = tempfile::tempdir().unwrap(); let globals = Globals { region: "us-east-1".to_string(), }; let (queue, join) = BackgroundQueueBuilder::new().build_boxed( Emf::all_validations("MyApp".into(), vec![vec![], vec!["region".into()]]) .merge_globals(globals) .output_to_makewriter( RollingFileAppender::new(Rotation::HOURLY, log_dir, "prefix.log") ) ); let mut metric = MyMetrics::default(); *metric.field += 1; queue.append_any(metric); ``` -------------------------------- ### Rust: Setting Per-Entry Dimensions for Metrics Source: https://github.com/awslabs/metrique/blob/main/metrique/docs/emf.md Demonstrates how to set per-entry dimensions using the `emf::dimension_sets` attribute in Rust. This feature is only available on the root metric. Validation of dimensions is not performed at compile time, so runtime checks with `Emf::all_validations` are recommended during development. The example shows metric declaration and how to format it using the `Emf` writer. ```rust use metrique::writer::format::Format; use metrique::emf::Emf; use metrique::unit_of_work::metrics; #[metrics( emf::dimension_sets = [ ["Status", "Operation"], ["Operation"] ], )] struct RequestMetrics { operation: &'static str, status: &'static str, number_of_ducks: usize, } #[test] fn test_metrics() { // Use all validations so that formatting produces a runtime error let mut emf = Emf::all_validations("MyApp".to_string(), vec![vec![]]); let mut output = vec![]; emf.format( &RequestMetrics { operation: "operation", status: "status", number_of_ducks: 1000, } .close(), &mut output, ) .unwrap(); } ``` -------------------------------- ### Test Emitted Metrics using TestEntrySink in Rust Source: https://github.com/awslabs/metrique/blob/main/metrique/README.md This Rust code demonstrates how to test emitted metrics using `metrique`'s `test_entry` functionality and `TestEntrySink`. It allows introspection of metrics without direct EMF reading. Ensure the `test-util` feature is enabled. The example shows how to create a sink, append metrics, and assert the values of emitted entries. ```rust # #[allow(clippy::test_attr_in_doctest)] use metrique::unit_of_work::metrics; use metrique::test_util::{self, TestEntrySink}; #[metrics(rename_all = "PascalCase")] struct RequestMetrics { operation: &'static str, number_of_ducks: usize } #[test] # fn test_in_doctests_is_a_lie() {} fn test_metrics () { let TestEntrySink { inspector, sink } = test_util::test_entry_sink(); let metrics = RequestMetrics { operation: "SayHello", number_of_ducks: 10 }.append_on_drop(sink); // In a real application, you would run some API calls, etc. let entries = inspector.entries(); assert_eq!(entries[0].values["Operation"], "SayHello"); assert_eq!(entries[0].metrics["NumberOfDucks"].as_u64(), 10); } ``` -------------------------------- ### Define and Emit Basic Metrics with Rust Source: https://context7.com/awslabs/metrique/llms.txt This snippet demonstrates how to define a metrics struct using the `#[metrics]` procedural macro and automatically emit these metrics when the struct goes out of scope. It shows initialization of a global sink, definition of metric fields with various types and units, and automatic timer functionality. The example also illustrates setting up an EMF output to stdout and handling a request with associated metrics. ```rust use metrique::unit_of_work::metrics; use metrique::unit::{Count, Millisecond}; use metrique::timers::{Timer, Timestamp}; use metrique::writer::{GlobalEntrySink, sink::global_entry_sink}; use std::time::SystemTime; // Define global sink global_entry_sink! { ServiceMetrics } #[metrics(rename_all = "PascalCase")] struct RequestMetrics { #[metrics(timestamp)] timestamp: SystemTime, operation: &'static str, request_id: String, #[metrics(unit = Count)] number_of_ducks: usize, time: Timer, // Auto-starts and stops timer } impl RequestMetrics { fn init(operation: &'static str, request_id: String) -> RequestMetricsGuard { Self { timestamp: SystemTime::now(), operation, request_id, number_of_ducks: 0, time: Timer::start_now(), } .append_on_drop(ServiceMetrics::sink()) } } #[tokio::main] async fn main() { use metrique::emf::Emf; use metrique::writer::{AttachGlobalEntrySinkExt, FormatExt}; // Initialize EMF output to stdout let _handle = ServiceMetrics::attach_to_stream( Emf::all_validations("MyApp".to_string(), vec![vec![]]) .output_to_makewriter(|| std::io::stdout().lock()) ); // Handle request - metrics automatically emit on drop let mut metrics = RequestMetrics::init("GetUser", "req-123".to_string()); metrics.number_of_ducks = 42; // Output: {"_aws":{"CloudWatchMetrics":[{"Namespace":"MyApp","Dimensions":[[]],"Metrics":[{"Name":"NumberOfDucks","Unit":"Count"}]}]},"Timestamp":1234567890},"Operation":"GetUser","RequestId":"req-123","NumberOfDucks":42} } ``` -------------------------------- ### Concurrent Metric Updates with Atomics in Rust Source: https://github.com/awslabs/metrique/blob/main/metrique/README.md Demonstrates using atomic field types like `Counter` to manage concurrent metric updates across multiple threads. It utilizes `metrique::metrics` macro for defining metrics and `Arc` for sharing metric handles. The example shows how to initialize metrics, clone handles for spawned threads, and accumulate values. Note that metrics are flushed only when all handles are dropped. ```rust use metrique::writer::GlobalEntrySink; use metrique::unit_of_work::metrics; use metrique::{Counter, ServiceMetrics}; use std::sync::Arc; #[metrics(rename_all = "PascalCase")] struct RequestMetrics { operation: &'static str, number_of_concurrent_ducks: Counter } impl RequestMetrics { fn init(operation: &'static str) -> RequestMetricsGuard { RequestMetrics { operation, number_of_concurrent_ducks: Default::default() }.append_on_drop(ServiceMetrics::sink()) } } fn count_concurrent_ducks() { let mut metrics = RequestMetrics::init("CountDucks"); // convenience function to wrap `entry` in an `Arc`. This makes a cloneable metrics handle. let handle = metrics.handle(); for i in 0..10 { let handle = handle.clone(); std::thread::spawn(move || { handle.number_of_concurrent_ducks.add(i); }); } // Each handle is keeping the metric entry alive! // The metric will not be flushed until all handles are dropped! // TODO: add an API to spawn a task that will force-flush the entry after a timeout. } ``` -------------------------------- ### Configure Global Metrics Sink with EMF Output in Rust Source: https://github.com/awslabs/metrique/blob/main/README.md Demonstrates how to initialize a global metrics sink that formats metrics using Amazon EMF and outputs them to a rolling file appender. This setup is crucial for controlling metric output destinations. Requires `metrique`, `metrique-writer`, and related components. ```rust 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", )), ) } ``` -------------------------------- ### Configure CloudWatch EMF with Dimensions and Destinations in Rust Source: https://context7.com/awslabs/metrique/llms.txt Sets up CloudWatch Embedded Metric Format (EMF) with dimension sets, global dimensions, and multiple output destinations like stdout, rolling files, and TCP sockets. This example demonstrates emitting metrics from a Rust application, suitable for various deployment environments. ```rust use metrique::emf::{Emf, HighStorageResolution, NoMetric}; use metrique::unit_of_work::metrics; use metrique::writer::{Entry, FormatExt, AttachGlobalEntrySinkExt, GlobalEntrySink, sink::global_entry_sink}; use std::time::SystemTime; global_entry_sink! { ServiceMetrics } #[metrics( emf::dimension_sets = [ ["Status", "Operation"], ["Operation"] ], rename_all = "PascalCase" )] struct RequestMetrics { #[metrics(timestamp)] timestamp: SystemTime, operation: &'static str, status: &'static str, count: usize, // Property-only field (not emitted as metric) no_metric: NoMetric, // High-resolution metric (1-second granularity) high_storage_resolution: HighStorageResolution, } #[derive(Entry)] #[entry(rename_all = "PascalCase")] struct Globals { region: String, version: String, } fn main() { let globals = Globals { region: "us-east-1".to_string(), version: "1.2.3".to_string(), }; // Option 1: stdout (for Lambda/ECS with CloudWatch Logs) let _handle = ServiceMetrics::attach_to_stream( Emf::all_validations("MyApp".to_string(), vec![vec!["Region".to_string()]]) .output_to_makewriter(|| std::io::stdout().lock()) .merge_globals(globals) ); // Option 2: Rolling file appender (for EC2/long-running services) use tracing_appender::rolling::{RollingFileAppender, Rotation}; use std::path::PathBuf; let _handle = ServiceMetrics::attach_to_stream( Emf::builder("MyApp".to_string(), vec![vec![]]) .log_group_name("MyLogGroup".to_string()) .build() .output_to_makewriter(RollingFileAppender::new( Rotation::MINUTELY, PathBuf::from("/var/log/myapp"), "metrics.log", )) ); // Option 3: TCP socket (for CloudWatch Agent/Firelens) use std::net::SocketAddr; tokio::runtime::Runtime::new().unwrap().block_on(async { let addr = SocketAddr::from(([127, 0, 0, 1], 25888)); let tcp = tokio::net::TcpStream::connect(addr).await.unwrap().into_std().unwrap(); let _stream = Emf::all_validations("MyApp".to_string(), vec![vec![]]) .output_to(tcp); }); } ``` -------------------------------- ### Rust: Initialize and Emit Application Metrics with Metrique Source: https://github.com/awslabs/metrique/blob/main/metrique/README.md Demonstrates setting up a global `ServiceMetrics` sink with a rolling file appender for EMF format. It defines a `RequestMetrics` struct with timestamp and timer fields, initializes metrics within a scope, and shows how metrics are automatically flushed when the scope is dropped. It also includes basic tracing subscriber initialization and a test case. ```rust use std::path::PathBuf; use metrique::unit_of_work::metrics; use metrique::timers::{Timestamp, Timer}; use metrique::unit::Millisecond; use metrique::ServiceMetrics; use metrique::writer::GlobalEntrySink; use metrique::writer::{AttachGlobalEntrySinkExt, FormatExt, sink::AttachHandle}; use metrique::emf::Emf; use tracing_appender::rolling::{RollingFileAppender, Rotation}; // define operation as an enum (you can also define operation as a &'static str) #[metrics(value(string))] #[derive(Copy, Clone)] enum Operation { CountDucks, } // define our metrics struct #[metrics(rename_all = "PascalCase")] struct RequestMetrics { operation: Operation, #[metrics(timestamp)] timestamp: Timestamp, number_of_ducks: usize, #[metrics(unit = Millisecond)] operation_time: Timer, } 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(), }.append_on_drop(ServiceMetrics::sink()) } } async fn count_ducks() { let mut metrics = RequestMetrics::init(Operation::CountDucks); metrics.number_of_ducks = 5; // metrics flushes as scope drops // timer records the total time until scope exits } fn initialize_metrics(service_log_dir: PathBuf) -> AttachHandle { // `metrique::ServiceMetrics` is a single global metric sink // defined by `metrique` that can be used by your application. // // If you want to have more than 1 stream of metrics in your // application (for example, to have separate streams of // metrics for your application's control and data planes), // you can define your own global entry sink (which will // behave exactly like `ServiceMetrics`) by using the // `metrique::writer::sink::global_entry_sink!` macro. // // See the examples in metrique/examples for that. // attach an EMF-formatted rolling file appender to `ServiceMetrics` // which will write the metrics asynchronously. 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", )), ) } #[tokio::main] async fn main() { // not strictly needed, but metrique will emit tracing errors // when entries are invalid and it's best to be able to see them. tracing_subscriber::fmt::init(); let _join = initialize_metrics("my/metrics/dir".into()); // ... // call count_ducks // for example count_ducks().await; } #[cfg(test)] mod test { #[tokio::test] async fn my_metrics_are_emitted() { let TestEntrySink { inspector, sink } = test_util::test_entry_sink(); let _guard = crate::ServiceMetrics::set_test_sink(sink); super::count_ducks().await; let entry = inspector.get(0); assert_eq!(entry.metrics["NumberOfDucks"], 5); } } ``` -------------------------------- ### Set Up Entry Sink with Background Queue and EMF Formatting Source: https://github.com/awslabs/metrique/blob/main/metrique-writer/README.md This Rust code demonstrates setting up an `EntrySink` without a global sink, suitable for applications that prefer explicit sink management. It configures a `BackgroundQueue` for asynchronous processing and uses `metrique-writer-format-emf` to format metrics into Amazon CloudWatch Embedded Metric Format (EMF). The output is directed to a rolling log file. ```rust use metrique_writer::{ Entry, EntrySink, format::FormatExt as _, sink::BackgroundQueue, unit::AsCount, }; use metrique_writer_format_emf::Emf; use tracing_appender::rolling::{RollingFileAppender, Rotation}; // Example setup for a non-global sink (details omitted as the provided code was incomplete) // let sink = BackgroundQueue::new( // Emf::all_validations("MyApp".into(), vec![vec![], vec!["region".into()]]) // .output_to_makewriter( // RollingFileAppender::new(Rotation::HOURLY, "/tmp", "prefix.log") // ) // ); // let mut metric = sink.append_on_drop_default::(); // *metric.field += 1; ``` -------------------------------- ### Metrique Proc Macro: Standard Metrics Struct Source: https://github.com/awslabs/metrique/blob/main/docs/aggregated-internals.md Example of a struct using the standard `#[metrics]` attribute. This struct can be emitted as metrics but not aggregated. ```rust // Standard metrics - can be emitted, not aggregated #[metrics] struct RequestMetrics { operation: &'static str, latency: Duration, } ``` -------------------------------- ### Metrique Proc Macro: Aggregation-Only Struct Source: https://github.com/awslabs/metrique/blob/main/docs/aggregated-internals.md Example of a struct using `#[metrics(aggregate_only)]`. This struct is designed solely for aggregation and cannot be emitted directly as metrics. ```rust // Aggregation-only - cannot be emitted directly, only aggregated #[metrics(aggregate_only)] struct RequestAggregator { #[metrics(key)] operation: &'static str, #[metrics(aggregate = Counter)] total_requests: u64, #[metrics(aggregate = Histogram)] latency_distribution: Duration, } ``` -------------------------------- ### Implement Custom Value Formatter for SystemTime Source: https://github.com/awslabs/metrique/blob/main/metrique/README.md Demonstrates creating a custom `ValueFormatter` for `SystemTime` to format it as a UTC RFC3339 string. This involves implementing the `format_value` method within a custom struct that derives `ValueFormatter`. ```rust use metrique::unit_of_work::metrics; use std::time::SystemTime; use chrono::{DateTime, Utc}; /// Format a SystemTime as UTC time struct AsUtcDate; // observe that `format_value` is a static method, so `AsUtcDate` // is never initialized. impl metrique::writer::value::ValueFormatter for AsUtcDate { fn format_value(writer: impl metrique::writer::ValueWriter, value: &SystemTime) { let datetime: DateTime = (*value).into(); writer.string(&datetime.to_rfc3339_opts(chrono::SecondsFormat::Secs, true)); } } #[metrics] struct MyMetric { #[metrics(format = AsUtcDate)] my_field: SystemTime, } ``` -------------------------------- ### Running Code with a Custom TimeSource in Rust Source: https://github.com/awslabs/metrique/blob/main/metrique-timesource/README.md Demonstrates using `with_time_source` to execute a closure with a specific `TimeSource` temporarily installed. This is useful for isolated testing scenarios. ```rust use metrique_timesource::{TimeSource, fakes::StaticTimeSource, time_source, with_time_source}; use std::time::UNIX_EPOCH; let ts = StaticTimeSource::at_time(UNIX_EPOCH); let custom = TimeSource::custom(ts); // Run code with the custom time source with_time_source(custom, || { // Code here will use the custom time source let now = time_source().system_time(); assert_eq!(now, UNIX_EPOCH); }); ``` -------------------------------- ### Metrique Macro Expansion for Metrics Struct Source: https://github.com/awslabs/metrique/blob/main/AGENTS.md This example demonstrates how the `#[metrics]` macro expands a user-defined struct with a `Histogram` field, applying unit information to the closed value. ```rust #[metrics] struct TestMetrics { #[metrics(unit = Millisecond)] latency: Histogram, } // Expanded to: struct TestMetricsEntry { latency: < as CloseValue>::Closed as AttachUnit>::Output } ``` -------------------------------- ### Get TimeSource with Optional Fallback in Rust Source: https://github.com/awslabs/metrique/blob/main/metrique-timesource/README.md Shows how to use `get_time_source` to prioritize an optionally provided `TimeSource` and fall back to the thread-local source if none is given. This is useful in builder patterns. ```rust use metrique_timesource::{TimeSource, get_time_source}; struct Thing { timesource: TimeSource } struct Builder { timesource: Option, } impl Builder { pub fn build(self) -> Thing { let timesource = get_time_source(self.timesource); Thing { timesource } } } ``` -------------------------------- ### Setting Global Dimensions with `metrique` Emf Builder Source: https://github.com/awslabs/metrique/blob/main/metrique/docs/emf.md Demonstrates how to initialize the `Emf` builder with a namespace and global dimensions. These dimensions will be applied to all metrics emitted by this sink. ```rust use metrique::emf::Emf; let emf = Emf::builder( // namespace "Ns".to_string(), // global dimensions vec![vec!["region".to_string()]], ) .build(); ``` -------------------------------- ### Testing TimeSource with Tokio Pause and Set in Rust Source: https://github.com/awslabs/metrique/blob/main/metrique-timesource/README.md This example demonstrates how to test time-dependent code using metrique-timesource with Tokio. It utilizes `tokio::time::pause`, `set_time_source` to override the time, and `advance` to manipulate time. ```rust use metrique_timesource::{TimeSource, time_source, set_time_source, Instant}; use std::time::{Duration, UNIX_EPOCH}; struct MyThingThatUsesTime { create_time: Instant } impl MyThingThatUsesTime { pub fn new() -> Self { Self { create_time: time_source().instant() } } } async fn test() { // Note: when using the tokio time source, you can't use multiple threads—tokio::time::pause only works // on the current-thread runtime. See https://docs.rs/tokio/latest/tokio/time/fn.pause.html tokio::time::pause(); let _guard = set_time_source(TimeSource::tokio(UNIX_EPOCH)); let my_thing = MyThingThatUsesTime::new(); assert_eq!(my_thing.create_time.elapsed(), Duration::from_secs(0)); tokio::time::advance(Duration::from_secs(5)).await; assert_eq!(my_thing.create_time.elapsed(), Duration::from_secs(5)); } tokio::runtime::Builder::new_current_thread().build().unwrap().block_on(test()) ``` -------------------------------- ### Control Test Queue with TestEntrySink or Global Entry Queues in Rust Source: https://github.com/awslabs/metrique/blob/main/metrique/README.md This Rust code illustrates two methods for controlling the metric emission queue during testing. The first method involves explicitly passing the queue when constructing metric objects. The second method utilizes `ServiceMetrics::set_test_sink` to globally set a test sink, simplifying queue management for testing purposes. ```rust use metrique::writer::GlobalEntrySink; use metrique::ServiceMetrics; use metrique::test_util::{self, TestEntrySink}; let TestEntrySink { inspector, sink } = test_util::test_entry_sink(); let _guard = ServiceMetrics::set_test_sink(sink); ``` -------------------------------- ### Release Process using release-plz Source: https://github.com/awslabs/metrique/blob/main/CONTRIBUTING.md Provides the command-line steps for updating the Cargo.toml and changelog using the `release-plz` tool, following conventional commit standards. This is part of the automated release process triggered by changes in Cargo.toml. ```bash cargo install release-plz --locked git checkout main && release-plz update # before committing, make sure that CHANGELOG.md contains an appropriate changelog git commit -a ``` -------------------------------- ### Configure Runtime Strategy Metrics in Rust Source: https://github.com/awslabs/metrique/blob/main/docs/aggregated.md Demonstrates how to use the `conf` parameter within the `metrics` macro to provide runtime configuration for metric strategies, such as histogram bucket limits. This pattern supports clean dependency injection without global state. ```rust use metrique::unit_of_work::metrics; use metrique::writer::aggregate::{Counter, Histogram, HistogramConfig}; use std::time::Duration; // Define your configuration struct #[derive(Clone)] struct MyMetricsConfig { histogram: HistogramConfig, } // Use conf parameter to pass configuration #[metrics(aggregate(conf = MyMetricsConfig))] struct RequestMetrics { #[metrics(key)] operation: &'static str, #[metrics(aggregate = Counter)] request_count: u64, #[metrics(aggregate = Histogram)] // Uses config.histogram latency: Duration, } // Create metrics with configuration let config = MyMetricsConfig { histogram: HistogramConfig { max_buckets: 500 }, }; let metrics = RequestMetrics::new_with_config( "get_user", 1, Duration::from_millis(50), &config ); ``` -------------------------------- ### Metrique Proc Macro: Aggregatable Metrics Struct Source: https://github.com/awslabs/metrique/blob/main/docs/aggregated-internals.md Example of a struct using `#[metrics(aggregate)]`. This allows the struct to be both emitted as metrics and aggregated. It uses `#[metrics(key)]` for identifying fields and `#[metrics(aggregate = ...)]` for aggregation strategies. ```rust // Aggregatable metrics - can be emitted AND aggregated #[metrics(aggregate)] struct RequestMetrics { #[metrics(key)] operation: &'static str, #[metrics(aggregate = Counter)] request_count: u64, #[metrics(aggregate = Histogram)] latency: Duration, } ``` -------------------------------- ### Rust: Unit Preservation in Aggregated Metrics Source: https://github.com/awslabs/metrique/blob/main/docs/aggregated.md Demonstrates how Metrique preserves units during aggregation, ensuring type safety. It shows examples of using units like `Megabyte` and `Millisecond` with `Counter` and `Histogram`, preventing incompatible unit aggregations at compile time. ```rust #[metrics(aggregate)] struct NetworkMetrics { #[metrics(aggregate = Counter, unit = Megabyte)] bytes_transferred: u64, #[metrics(aggregate = Histogram, unit = Millisecond)] request_latency: u64, } ``` -------------------------------- ### Implement Sampling with Congressional Sampler in Rust Source: https://github.com/awslabs/metrique/blob/main/metrique/README.md This Rust code demonstrates how to use the `CongressSample` for efficient metric sampling. It configures two streams: one for a comprehensive log of record and another, sampled stream for CloudWatch. The congressional sampler is keyed by operation and status code to improve accuracy for low-frequency events. Ensure `with_sampling` is called before the sampler. ```rust use metrique::unit_of_work::metrics; use metrique::emf::Emf; use metrique::writer::{AttachGlobalEntrySinkExt, FormatExt, GlobalEntrySink}; use metrique::writer::sample::SampledFormatExt; use metrique::writer::stream::tee; use metrique::ServiceMetrics; use tracing_appender::rolling::{RollingFileAppender, Rotation}; # let service_log_dir = "./service_log"; # let metrics_log_dir = "./metrics_log"; #[metrics(value(string))] enum Operation { CountDucks, // ... } #[metrics(rename_all="PascalCase")] struct RequestMetrics { #[metrics(sample_group)] operation: Operation, #[metrics(sample_group)] status_code: &'static str, number_of_ducks: u32, exception: Option, } let _join_service_metrics = ServiceMetrics::attach_to_stream( tee( // non-uploaded, archived log of record Emf::all_validations("MyNS".to_string(), /* dimensions */ vec![vec![], vec!["Operation".to_string()]]) .output_to_makewriter(RollingFileAppender::new( Rotation::MINUTELY, service_log_dir, "service_log.log", )), // sampled log, will be uploaded to CloudWatch Emf::all_validations("MyNS".to_string(), /* dimensions */ vec![vec![], vec!["Operation".to_string()]]) .with_sampling() .sample_by_congress_at_fixed_entries_per_second(100) .output_to_makewriter(RollingFileAppender::new( Rotation::MINUTELY, metrics_log_dir, "metric_log.log", )), ) ); let metric = RequestMetrics { operation: Operation::CountDucks, status_code: "OK", number_of_ducks: 2, exception: None, }.append_on_drop(ServiceMetrics::sink()); // _join_service_metrics drop (e.g. during service shutdown) blocks until the queue is drained ``` -------------------------------- ### Rust Histogram Example: Collecting Backend Latency Source: https://github.com/awslabs/metrique/blob/main/metrique-aggregation/README.md Demonstrates how to use the `Histogram` type in Rust to collect and aggregate multiple observations of backend latency within a query. The `Histogram` collects values and emits a single distribution metric when it goes out of scope. ```rust use metrique::unit_of_work::metrics; use metrique_aggregation::histogram::Histogram; use metrique_writer::unit::Millisecond; use std::time::Duration; #[metrics(rename_all = "PascalCase")] struct QueryMetrics { query_id: String, #[metrics(unit = Millisecond)] backend_latency: Histogram, } fn execute_query(query_id: String) { let mut metrics = QueryMetrics { query_id, backend_latency: Histogram::default(), }; // Record multiple observations metrics.backend_latency.add_value(Duration::from_millis(45)); metrics.backend_latency.add_value(Duration::from_millis(67)); metrics.backend_latency.add_value(Duration::from_millis(52)); // When metrics drops, emits a single entry with the distribution } ``` -------------------------------- ### Combine Renaming Strategies (Rust) Source: https://github.com/awslabs/metrique/blob/main/metrique/README.md Shows how to combine different renaming strategies, including `rename_all` on structs, `prefix` on nested structs, and `name` on individual fields. Field-level renames take precedence over struct-level rules. ```rust use metrique::unit_of_work::metrics; #[metrics(rename_all = "kebab-case")] struct Metrics { // Will appear as "foo-bar" in metrics output foo_bar: usize, // Will appear as "custom_name" in metrics output (not kebab-cased) #[metrics(name = "custom_name")] overridden_field: &'static str, // Nested metrics can have their own renaming rules #[metrics(flatten, prefix="his-")] nested: PrefixedMetrics, } #[metrics(rename_all = "PascalCase", prefix = "api_")] struct PrefixedMetrics { // Will appear as "his-ApiLatency" in metrics output (explicit rename_all overrides the parent) latency: usize, // Will appear as "his-exact_name" in metrics output (overrides both struct prefix and case, but not external prefix) #[metrics(name = "exact_name")] response_time: usize, } ``` -------------------------------- ### Renaming Metric Fields with Kebab-Case in Rust Source: https://github.com/awslabs/metrique/blob/main/metrique/README.md Demonstrates renaming all metric fields within a struct to `kebab-case` using the `#[metrics(rename_all = "kebab-case")]` attribute. This is useful for enforcing a consistent naming convention in the output. The example shows how `operation_name` becomes `operation-name` and `request_size` becomes `request-size`. ```rust use metrique::unit_of_work::metrics; // All fields will use kebab-case in the output #[metrics(rename_all = "kebab-case")] struct RequestMetrics { // Will appear as "operation-name" in metrics output operation_name: &'static str, // Will appear as "request-size" in metrics output request_size: usize } ``` -------------------------------- ### Configure Immediate Flushing Metric Sink Source: https://github.com/awslabs/metrique/blob/main/metrique/README.md Configures a metric sink using `FlushImmediately` for environments where background threads are not suitable, like AWS Lambda. This sink blocks while writing each entry and flushes metrics immediately upon request completion. ```rust use metrique::emf::Emf; use metrique::ServiceMetrics; use metrique::writer::{AttachGlobalEntrySink, FormatExt, GlobalEntrySink}; use metrique::writer::sink::FlushImmediately; use metrique::unit_of_work::metrics; #[metrics] struct MyMetrics { value: u64, } fn main() { let sink = FlushImmediately::new_boxed( Emf::no_validations( "MyNS".to_string(), vec![vec![/*your dimensions here */]], ) .output_to(std::io::stdout()), ); let _handle = ServiceMetrics::attach((sink, ())); handle_request(); } fn handle_request() { let mut metrics = MyMetrics { value: 0 }.append_on_drop(ServiceMetrics::sink()); metrics.value += 1; // request will be flushed immediately here, as the request is dropped } ``` -------------------------------- ### Prefixing Metric Fields in Rust Source: https://github.com/awslabs/metrique/blob/main/metrique/README.md Shows how to add a common prefix to all metric fields within a struct using the `#[metrics(prefix = "...")]` attribute. This example applies an `api_` prefix to `latency` and `errors` fields, resulting in output names like `ApiLatency` and `ApiErrors` when combined with `rename_all = "PascalCase"`. ```rust use metrique::unit_of_work::metrics; // All fields will be prefixed with "api_" #[metrics(rename_all = "PascalCase", prefix = "api_")] struct ApiMetrics { // Will appear as "ApiLatency" in metrics output latency: usize, // Will appear as "ApiErrors" in metrics output errors: usize } ``` -------------------------------- ### Implement Counter Aggregation using AggregateValue in Rust Source: https://github.com/awslabs/metrique/blob/main/docs/aggregated-internals.md This Rust code provides an example implementation of the `AggregateValue` trait for a `Counter`. It defines the aggregated type as `T` (the same as the input type) and implements `init` to return the default value and `aggregate` to perform addition assignment. This enables summing up values for a counter metric. ```rust // Counter sums values impl AggregateValue for Counter { type Aggregated = T; fn init() -> T { T::default() } fn aggregate(accum: &mut T, value: &T) { *accum += *value; } } ``` -------------------------------- ### Rust: Sink-Level Aggregation with AggregatingEntrySink Source: https://github.com/awslabs/metrique/blob/main/docs/aggregated.md Demonstrates using `AggregatingEntrySink` to aggregate metrics for high-rate events, providing true unsampled metrics. It shows how to wrap a base sink, configure aggregation, and emit metrics automatically on drop. This approach is efficient for scenarios with many small, frequent events. ```rust use metrique::unit_of_work::metrics; use metrique::writer::merge::{AggregatingEntrySink, Counter, Histogram}; use metrique::instrument::Instrumented; use std::time::{Duration, Instant}; // Background queue processor - one metric per processed item #[metrics(aggregate)] struct QueueItem { #[metrics(key)] item_type: &'static str, #[metrics(key)] priority: u8, #[metrics(aggregate = Counter)] items_processed: u64, #[metrics(aggregate = Histogram)] processing_time: Duration, #[metrics(aggregate = Counter)] processing_errors: u64, } async fn setup_queue_processor() { // Wrap your normal sink with aggregation let base_sink = ServiceMetrics::sink(); let aggregating_sink = AggregatingEntrySink::with_config( base_sink, AggregateConfig { max_entries: 1000, raw_events_per_second: 10.0, // Sample 10 raw events per second } ); // Process queue items as they arrive while let Ok(item) = queue.recv().await { // Create metrics with append_on_drop for automatic emission let mut queue_metrics = QueueItem { item_type: item.type_name(), priority: item.priority, items_processed: 1, processing_time: Duration::ZERO, processing_errors: 0, }.append_on_drop(&aggregating_sink); // Process item and capture timing let start = Instant::now(); let result = process_item(item).await; queue_metrics.processing_time = start.elapsed(); if result.is_err() { queue_metrics.processing_errors = 1; } // Metrics automatically aggregated and emitted when dropped } // Periodically flushes aggregated results + sampled raw events } ```