diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b3aafd30..1eeb1f2f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,8 +44,14 @@ jobs: name: Test ${{ matrix.rust_version }} runs-on: ubuntu-latest strategy: + # 1.70 is the MSRV for the project, which currently does not match the version specified in + # the rust-toolchain.toml file as metrics-observer requires 1.74 to build. See + # https://github.com/metrics-rs/metrics/pull/505#discussion_r1724092556 for more information. matrix: - rust_version: ['stable', 'nightly'] + rust_version: ['stable', 'nightly', '1.70'] + include: + - rust_version: '1.70' + exclude-packages: '--exclude metrics-observer' steps: - uses: actions/checkout@v3 - name: Install Protobuf Compiler @@ -53,7 +59,7 @@ jobs: - name: Install Rust ${{ matrix.rust_version }} run: rustup install ${{ matrix.rust_version }} - name: Run Tests - run: cargo +${{ matrix.rust_version }} test --all-features --workspace + run: cargo +${{ matrix.rust_version }} test --all-features --workspace ${{ matrix.exclude-packages }} docs: runs-on: ubuntu-latest env: diff --git a/clippy.toml b/clippy.toml index 5e4d2492..be264b66 100644 --- a/clippy.toml +++ b/clippy.toml @@ -1 +1,2 @@ too-many-lines-threshold = 150 +ignore-interior-mutability = ["metrics::key::Key"] diff --git a/metrics-exporter-prometheus/CHANGELOG.md b/metrics-exporter-prometheus/CHANGELOG.md index 05be15d3..2f2495e0 100644 --- a/metrics-exporter-prometheus/CHANGELOG.md +++ b/metrics-exporter-prometheus/CHANGELOG.md @@ -8,6 +8,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] - ReleaseDate +### Added + +- Added `Debug` derive to numerous types. ([#504](https://github.com/metrics-rs/metrics/pull/504)) + +### Changed + +- Fixed a number of Clippy lints. ([#510](https://github.com/metrics-rs/metrics/pull/510)) + ## [0.15.3] - 2024-07-13 Republishing 0.15.2 as 0.15.3 to fix an incorrect publish. diff --git a/metrics-exporter-prometheus/src/common.rs b/metrics-exporter-prometheus/src/common.rs index 94ff6ab6..51f9bad9 100644 --- a/metrics-exporter-prometheus/src/common.rs +++ b/metrics-exporter-prometheus/src/common.rs @@ -80,6 +80,7 @@ pub enum BuildError { ZeroBucketDuration, } +#[derive(Debug)] pub struct Snapshot { pub counters: HashMap, u64>>, pub gauges: HashMap, f64>>, diff --git a/metrics-exporter-prometheus/src/distribution.rs b/metrics-exporter-prometheus/src/distribution.rs index 5a8d0f0b..cf997201 100644 --- a/metrics-exporter-prometheus/src/distribution.rs +++ b/metrics-exporter-prometheus/src/distribution.rs @@ -15,7 +15,7 @@ const DEFAULT_SUMMARY_BUCKET_COUNT: NonZeroU32 = match NonZeroU32::new(3) { const DEFAULT_SUMMARY_BUCKET_DURATION: Duration = Duration::from_secs(20); /// Distribution type. -#[derive(Clone)] +#[derive(Clone, Debug)] pub enum Distribution { /// A Prometheus histogram. /// @@ -33,7 +33,10 @@ pub enum Distribution { impl Distribution { /// Creates a histogram distribution. - #[warn(clippy::missing_panics_doc)] + /// + /// # Panics + /// + /// Panics if `buckets` is empty. pub fn new_histogram(buckets: &[f64]) -> Distribution { let hist = Histogram::new(buckets).expect("buckets should never be empty"); Distribution::Histogram(hist) @@ -134,14 +137,14 @@ impl DistributionBuilder { } } -#[derive(Clone)] +#[derive(Clone, Debug)] struct Bucket { begin: Instant, summary: Summary, } /// A `RollingSummary` manages a list of [Summary] so that old results can be expired. -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct RollingSummary { // Buckets are ordered with the latest buckets first. The buckets are kept in alignment based // on the instant of the first added bucket and the bucket_duration. There may be gaps in the @@ -299,8 +302,11 @@ mod tests { let snapshot = summary.snapshot(clock.now()); assert_eq!(0, snapshot.count()); - assert_eq!(f64::INFINITY, snapshot.min()); - assert_eq!(f64::NEG_INFINITY, snapshot.max()); + #[allow(clippy::float_cmp)] + { + assert_eq!(f64::INFINITY, snapshot.min()); + assert_eq!(f64::NEG_INFINITY, snapshot.max()); + } assert_eq!(None, snapshot.quantile(0.5)); } @@ -318,8 +324,11 @@ mod tests { let snapshot = summary.snapshot(clock.now()); - assert_eq!(42.0, snapshot.min()); - assert_eq!(42.0, snapshot.max()); + #[allow(clippy::float_cmp)] + { + assert_eq!(42.0, snapshot.min()); + assert_eq!(42.0, snapshot.max()); + } // 42 +/- (42 * 0.0001) assert!(Some(41.9958) < snapshot.quantile(0.5)); assert!(Some(42.0042) > snapshot.quantile(0.5)); diff --git a/metrics-exporter-prometheus/src/exporter/builder.rs b/metrics-exporter-prometheus/src/exporter/builder.rs index 7565ba2b..9ed56135 100644 --- a/metrics-exporter-prometheus/src/exporter/builder.rs +++ b/metrics-exporter-prometheus/src/exporter/builder.rs @@ -33,6 +33,7 @@ use super::ExporterConfig; use super::ExporterFuture; /// Builder for creating and installing a Prometheus recorder/exporter. +#[derive(Debug)] pub struct PrometheusBuilder { #[cfg_attr(not(any(feature = "http-listener", feature = "push-gateway")), allow(dead_code))] exporter_config: ExporterConfig, @@ -560,8 +561,7 @@ mod tests { gauge1.set(-3.14); let rendered = handle.render(); let expected_gauge = format!( - "{}# TYPE basic_gauge gauge\nbasic_gauge{{wutang=\"forever\"}} -3.14\n\n", - expected_counter + "{expected_counter}# TYPE basic_gauge gauge\nbasic_gauge{{wutang=\"forever\"}} -3.14\n\n", ); assert_eq!(rendered, expected_gauge); @@ -579,7 +579,7 @@ mod tests { "basic_histogram_count 1\n", "\n" ); - let expected_histogram = format!("{}{}", expected_gauge, histogram_data); + let expected_histogram = format!("{expected_gauge}{histogram_data}"); assert_eq!(rendered, expected_histogram); } diff --git a/metrics-exporter-prometheus/src/exporter/http_listener.rs b/metrics-exporter-prometheus/src/exporter/http_listener.rs index e820cec9..1a7089b9 100644 --- a/metrics-exporter-prometheus/src/exporter/http_listener.rs +++ b/metrics-exporter-prometheus/src/exporter/http_listener.rs @@ -32,6 +32,7 @@ enum ListenerType { } /// Error type for HTTP listening. +#[derive(Debug)] pub enum HttpListeningError { Hyper(hyper::Error), Io(std::io::Error), @@ -59,11 +60,11 @@ impl HttpListeningExporter { continue; } }; - self.process_tcp_stream(stream).await; + self.process_tcp_stream(stream); } } - async fn process_tcp_stream(&self, stream: TcpStream) { + fn process_tcp_stream(&self, stream: TcpStream) { let is_allowed = self.check_tcp_allowed(&stream); let handle = self.handle.clone(); let service = service_fn(move |req: Request| { @@ -107,12 +108,12 @@ impl HttpListeningExporter { continue; } }; - self.process_uds_stream(stream).await; + self.process_uds_stream(stream); } } #[cfg(feature = "uds-listener")] - async fn process_uds_stream(&self, stream: UnixStream) { + fn process_uds_stream(&self, stream: UnixStream) { let handle = self.handle.clone(); let service = service_fn(move |req: Request| { let handle = handle.clone(); diff --git a/metrics-exporter-prometheus/src/exporter/mod.rs b/metrics-exporter-prometheus/src/exporter/mod.rs index c8acbbac..d10c0336 100644 --- a/metrics-exporter-prometheus/src/exporter/mod.rs +++ b/metrics-exporter-prometheus/src/exporter/mod.rs @@ -14,6 +14,7 @@ use hyper::Uri; /// Error types possible from an exporter #[cfg(any(feature = "http-listener", feature = "push-gateway"))] +#[derive(Debug)] pub enum ExporterError { #[cfg(feature = "http-listener")] HttpListener(HttpListeningError), @@ -24,14 +25,14 @@ pub enum ExporterError { pub type ExporterFuture = Pin> + Send + 'static>>; #[cfg(feature = "http-listener")] -#[derive(Clone)] +#[derive(Clone, Debug)] enum ListenDestination { Tcp(SocketAddr), #[cfg(feature = "uds-listener")] Uds(std::path::PathBuf), } -#[derive(Clone)] +#[derive(Clone, Debug)] enum ExporterConfig { // Run an HTTP listener on the given `listen_address`. #[cfg(feature = "http-listener")] diff --git a/metrics-exporter-prometheus/src/exporter/push_gateway.rs b/metrics-exporter-prometheus/src/exporter/push_gateway.rs index 3c349918..a1c2a4e7 100644 --- a/metrics-exporter-prometheus/src/exporter/push_gateway.rs +++ b/metrics-exporter-prometheus/src/exporter/push_gateway.rs @@ -94,7 +94,7 @@ fn basic_auth(username: &str, password: Option<&str>) -> HeaderValue { header } -#[cfg(all(test))] +#[cfg(test)] mod tests { use super::basic_auth; diff --git a/metrics-exporter-prometheus/src/formatting.rs b/metrics-exporter-prometheus/src/formatting.rs index 75f61524..ea26219f 100644 --- a/metrics-exporter-prometheus/src/formatting.rs +++ b/metrics-exporter-prometheus/src/formatting.rs @@ -110,17 +110,18 @@ pub fn write_metric_line( /// [data model]: https://prometheus.io/docs/concepts/data_model/#metric-names-and-labels pub fn sanitize_metric_name(name: &str) -> String { // The first character must be [a-zA-Z_:], and all subsequent characters must be [a-zA-Z0-9_:]. - let mut out = String::with_capacity(name.len()); - let mut is_invalid: fn(char) -> bool = invalid_metric_name_start_character; - for c in name.chars() { - if is_invalid(c) { - out.push('_'); - } else { - out.push(c); - } - is_invalid = invalid_metric_name_character; - } - out + name.chars() + .enumerate() + .map(|(i, c)| { + if i == 0 && valid_metric_name_start_character(c) + || i != 0 && valid_metric_name_character(c) + { + c + } else { + '_' + } + }) + .collect() } /// Sanitizes a label key to be valid under the Prometheus [data model]. @@ -128,17 +129,18 @@ pub fn sanitize_metric_name(name: &str) -> String { /// [data model]: https://prometheus.io/docs/concepts/data_model/#metric-names-and-labels pub fn sanitize_label_key(key: &str) -> String { // The first character must be [a-zA-Z_], and all subsequent characters must be [a-zA-Z0-9_]. - let mut out = String::with_capacity(key.len()); - let mut is_invalid: fn(char) -> bool = invalid_label_key_start_character; - for c in key.chars() { - if is_invalid(c) { - out.push('_'); - } else { - out.push(c); - } - is_invalid = invalid_label_key_character; - } - out + key.chars() + .enumerate() + .map(|(i, c)| { + if i == 0 && valid_label_key_start_character(c) + || i != 0 && valid_label_key_character(c) + { + c + } else { + '_' + } + }) + .collect() } /// Sanitizes a label value to be valid under the Prometheus [data model]. @@ -209,35 +211,35 @@ fn sanitize_label_value_or_description(value: &str, is_desc: bool) -> String { } #[inline] -fn invalid_metric_name_start_character(c: char) -> bool { +fn valid_metric_name_start_character(c: char) -> bool { // Essentially, needs to match the regex pattern of [a-zA-Z_:]. - !(c.is_ascii_alphabetic() || c == '_' || c == ':') + c.is_ascii_alphabetic() || c == '_' || c == ':' } #[inline] -fn invalid_metric_name_character(c: char) -> bool { +fn valid_metric_name_character(c: char) -> bool { // Essentially, needs to match the regex pattern of [a-zA-Z0-9_:]. - !(c.is_ascii_alphanumeric() || c == '_' || c == ':') + c.is_ascii_alphanumeric() || c == '_' || c == ':' } #[inline] -fn invalid_label_key_start_character(c: char) -> bool { +fn valid_label_key_start_character(c: char) -> bool { // Essentially, needs to match the regex pattern of [a-zA-Z_]. - !(c.is_ascii_alphabetic() || c == '_') + c.is_ascii_alphabetic() || c == '_' } #[inline] -fn invalid_label_key_character(c: char) -> bool { +fn valid_label_key_character(c: char) -> bool { // Essentially, needs to match the regex pattern of [a-zA-Z0-9_]. - !(c.is_ascii_alphanumeric() || c == '_') + c.is_ascii_alphanumeric() || c == '_' } #[cfg(test)] mod tests { use crate::formatting::{ - invalid_label_key_character, invalid_label_key_start_character, - invalid_metric_name_character, invalid_metric_name_start_character, sanitize_description, - sanitize_label_key, sanitize_label_value, sanitize_metric_name, + sanitize_description, sanitize_label_key, sanitize_label_value, sanitize_metric_name, + valid_label_key_character, valid_label_key_start_character, valid_metric_name_character, + valid_metric_name_start_character, }; use proptest::prelude::*; @@ -321,11 +323,11 @@ mod tests { let as_chars = result.chars().collect::>(); if let Some(c) = as_chars.first() { - assert_eq!(false, invalid_metric_name_start_character(*c), + assert!(valid_metric_name_start_character(*c), "first character of metric name was not valid"); } - assert!(!as_chars.iter().any(|c| invalid_metric_name_character(*c)), + assert!(as_chars.iter().all(|c| valid_metric_name_character(*c)), "invalid character in metric name"); } @@ -335,7 +337,7 @@ mod tests { let as_chars = result.chars().collect::>(); if let Some(c) = as_chars.first() { - assert_eq!(false, invalid_label_key_start_character(*c), + assert!(valid_label_key_start_character(*c), "first character of label key was not valid"); } @@ -353,7 +355,7 @@ mod tests { } }*/ - assert!(!as_chars.iter().any(|c| invalid_label_key_character(*c)), + assert!(as_chars.iter().all(|c| valid_label_key_character(*c)), "invalid character in label key"); } @@ -369,7 +371,7 @@ mod tests { let as_chars = delayered_backslashes.chars().collect::>(); // If the first character is a double quote, then we messed up. - assert!(as_chars.first().map(|c| *c != '"').unwrap_or(true), + assert!(as_chars.first().map_or(true, |c| *c != '"'), "first character cannot be a double quote: {}", result); // Now look for unescaped characters in the rest of the string, in a windowed fashion. diff --git a/metrics-exporter-prometheus/src/recorder.rs b/metrics-exporter-prometheus/src/recorder.rs index 21b2ea79..7d49f688 100644 --- a/metrics-exporter-prometheus/src/recorder.rs +++ b/metrics-exporter-prometheus/src/recorder.rs @@ -15,6 +15,7 @@ use crate::formatting::{ }; use crate::registry::GenerationalAtomicStorage; +#[derive(Debug)] pub(crate) struct Inner { pub registry: Registry, pub recency: Recency, @@ -214,6 +215,7 @@ impl Inner { /// Most users will not need to interact directly with the recorder, and can simply deal with the /// builder methods on [`PrometheusBuilder`](crate::PrometheusBuilder) for building and installing /// the recorder/exporter. +#[derive(Debug)] pub struct PrometheusRecorder { inner: Arc, } @@ -275,7 +277,7 @@ impl Recorder for PrometheusRecorder { /// handled directly by the HTTP listener, or push gateway background task. [`PrometheusHandle`] /// allows rendering a snapshot of the current metrics stored by an installed [`PrometheusRecorder`] /// as a payload conforming to the Prometheus exposition format. -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct PrometheusHandle { inner: Arc, } diff --git a/metrics-exporter-prometheus/src/registry.rs b/metrics-exporter-prometheus/src/registry.rs index c6001743..ea5b470a 100644 --- a/metrics-exporter-prometheus/src/registry.rs +++ b/metrics-exporter-prometheus/src/registry.rs @@ -7,6 +7,7 @@ use quanta::Instant; pub type GenerationalAtomicStorage = GenerationalStorage; /// Atomic metric storage for the prometheus exporter. +#[derive(Debug)] pub struct AtomicStorage; impl metrics_util::registry::Storage for AtomicStorage { @@ -28,6 +29,7 @@ impl metrics_util::registry::Storage for AtomicStorage { } /// An `AtomicBucket` newtype wrapper that tracks the time of value insertion. +#[derive(Debug)] pub struct AtomicBucketInstant { inner: AtomicBucket<(T, Instant)>, } diff --git a/metrics-exporter-prometheus/tests/http_listener_integration_test.rs b/metrics-exporter-prometheus/tests/http_listener_integration_test.rs index 93f69f08..ffbd3efa 100644 --- a/metrics-exporter-prometheus/tests/http_listener_integration_test.rs +++ b/metrics-exporter-prometheus/tests/http_listener_integration_test.rs @@ -36,7 +36,7 @@ mod http_listener_test { let labels = vec![Label::new("wutang", "forever")]; let key = Key::from_parts("basic_gauge", labels); let gauge = recorder.register_gauge(&key, &METADATA); - gauge.set(-3.14); + gauge.set(-1.23); runtime.spawn(exporter); //async { exporter.await}); tokio::time::sleep(Duration::from_millis(200)).await; @@ -48,7 +48,7 @@ mod http_listener_test { let (status, body) = read_from(uri).await; assert_eq!(status, StatusCode::OK); - assert!(body.contains("basic_gauge{wutang=\"forever\"} -3.14")); + assert!(body.contains("basic_gauge{wutang=\"forever\"} -1.23")); }); } diff --git a/metrics-exporter-tcp/CHANGELOG.md b/metrics-exporter-tcp/CHANGELOG.md index 2aeb41a2..90736260 100644 --- a/metrics-exporter-tcp/CHANGELOG.md +++ b/metrics-exporter-tcp/CHANGELOG.md @@ -8,6 +8,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] - ReleaseDate +### Added + +- Added `Debug` derive to numerous types. ([#504](https://github.com/metrics-rs/metrics/pull/504)) + +### Changed + +- Updated `mio` to `1.0`. + ## [0.10.0] - 2024-05-27 ### Changed diff --git a/metrics-exporter-tcp/Cargo.toml b/metrics-exporter-tcp/Cargo.toml index 893df569..0a002a13 100644 --- a/metrics-exporter-tcp/Cargo.toml +++ b/metrics-exporter-tcp/Cargo.toml @@ -22,7 +22,7 @@ bytes = { version = "1", default-features = false } crossbeam-channel = { version = "0.5", default-features = false, features = ["std"] } prost = { version = "0.12", default-features = false } prost-types = { version = "0.12", default-features = false, features = ["std"] } -mio = { version = "0.8", default-features = false, features = ["os-poll", "net"] } +mio = { version = "1.0", default-features = false, features = ["os-poll", "net"] } tracing = { version = "0.1", default-features = false, features = ["attributes"] } [build-dependencies] diff --git a/metrics-exporter-tcp/src/lib.rs b/metrics-exporter-tcp/src/lib.rs index 7a81e44d..4cc27625 100644 --- a/metrics-exporter-tcp/src/lib.rs +++ b/metrics-exporter-tcp/src/lib.rs @@ -137,6 +137,7 @@ impl std::error::Error for Error { } } +#[derive(Debug)] struct State { client_count: AtomicUsize, should_send: AtomicBool, @@ -188,6 +189,7 @@ impl State { } } +#[derive(Debug)] struct Handle { key: Key, state: Arc, @@ -230,11 +232,13 @@ impl HistogramFn for Handle { } /// A TCP recorder. +#[derive(Debug)] pub struct TcpRecorder { state: Arc, } /// Builder for creating and installing a TCP recorder/exporter. +#[derive(Debug)] pub struct TcpBuilder { listen_addr: SocketAddr, buffer_size: Option, diff --git a/metrics-observer/CHANGELOG.md b/metrics-observer/CHANGELOG.md index 7c811932..402ace2c 100644 --- a/metrics-observer/CHANGELOG.md +++ b/metrics-observer/CHANGELOG.md @@ -8,6 +8,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] - ReleaseDate +### Changed + +- Switched from `tui` to `ratatui`. ([#505](https://github.com/metrics-rs/metrics/pull/505)) +- Bump MSRV to 1.74.0. + ## [0.4.0] - 2024-05-27 ### Changed diff --git a/metrics-observer/Cargo.toml b/metrics-observer/Cargo.toml index 57df5494..06415f5b 100644 --- a/metrics-observer/Cargo.toml +++ b/metrics-observer/Cargo.toml @@ -3,7 +3,7 @@ name = "metrics-observer" version = "0.4.0" authors = ["Toby Lawrence "] edition = "2018" -rust-version = "1.70.0" +rust-version = "1.74.0" license = "MIT" @@ -23,8 +23,7 @@ bytes = { version = "1", default-features = false } crossbeam-channel = { version = "0.5", default-features = false, features = ["std"] } prost = { version = "0.12", default-features = false } prost-types = { version = "0.12", default-features = false } -tui = { version = "0.19", default-features = false, features = ["termion"] } -termion = { version = "2", default-features = false } +ratatui = { version = "0.28.0", default-features = false, features = ["crossterm"] } chrono = { version = "0.4", default-features = false, features = ["clock"] } [build-dependencies] diff --git a/metrics-observer/src/input.rs b/metrics-observer/src/input.rs index 65fd27a0..a150c1d6 100644 --- a/metrics-observer/src/input.rs +++ b/metrics-observer/src/input.rs @@ -1,37 +1,18 @@ use std::io; -use std::thread; use std::time::Duration; -use crossbeam_channel::{bounded, Receiver, RecvTimeoutError, TrySendError}; -use termion::event::Key; -use termion::input::TermRead; +use ratatui::crossterm::event::{self, Event, KeyEvent, KeyEventKind}; -pub struct InputEvents { - rx: Receiver, -} +pub struct InputEvents; impl InputEvents { - pub fn new() -> InputEvents { - let (tx, rx) = bounded(1); - thread::spawn(move || { - let stdin = io::stdin(); - for key in stdin.keys().flatten() { - // If our queue is full, we don't care. The user can just press the key again. - if let Err(TrySendError::Disconnected(_)) = tx.try_send(key) { - eprintln!("input event channel disconnected"); - return; - } + pub fn next() -> io::Result> { + if event::poll(Duration::from_secs(1))? { + match event::read()? { + Event::Key(key) if key.kind == KeyEventKind::Press => return Ok(Some(key)), + _ => {} } - }); - - InputEvents { rx } - } - - pub fn next(&mut self) -> Result, RecvTimeoutError> { - match self.rx.recv_timeout(Duration::from_secs(1)) { - Ok(key) => Ok(Some(key)), - Err(RecvTimeoutError::Timeout) => Ok(None), - Err(e) => Err(e), } + Ok(None) } } diff --git a/metrics-observer/src/main.rs b/metrics-observer/src/main.rs index dff15517..9f079529 100644 --- a/metrics-observer/src/main.rs +++ b/metrics-observer/src/main.rs @@ -1,16 +1,20 @@ -use std::fmt; use std::num::FpCategory; use std::time::Duration; use std::{error::Error, io}; +use std::{fmt, io::Stdout}; use chrono::Local; use metrics::Unit; -use termion::{event::Key, input::MouseTerminal, raw::IntoRawMode, screen::IntoAlternateScreen}; -use tui::{ - backend::TermionBackend, +use ratatui::{ + backend::CrosstermBackend, + crossterm::{ + event::KeyCode, + execute, + terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, + }, layout::{Constraint, Direction, Layout}, style::{Color, Modifier, Style}, - text::{Span, Spans}, + text::{Line, Span}, widgets::{Block, Borders, List, ListItem, Paragraph, Wrap}, Terminal, }; @@ -27,23 +31,23 @@ mod selector; use self::selector::Selector; fn main() -> Result<(), Box> { - let stdout = io::stdout().into_raw_mode()?; - let stdout = MouseTerminal::from(stdout).into_alternate_screen()?; - let backend = TermionBackend::new(stdout); - let mut terminal = Terminal::new(backend)?; + let terminal = init_terminal()?; + let result = run(terminal); + restore_terminal()?; + result +} - let mut events = InputEvents::new(); +fn run(mut terminal: Terminal>) -> Result<(), Box> { let address = std::env::args().nth(1).unwrap_or_else(|| "127.0.0.1:5000".to_owned()); let client = metrics_inner::Client::new(address); let mut selector = Selector::new(); - loop { terminal.draw(|f| { let chunks = Layout::default() .direction(Direction::Vertical) .margin(1) .constraints([Constraint::Length(4), Constraint::Percentage(90)].as_ref()) - .split(f.size()); + .split(f.area()); let current_dt = Local::now().format(" (%Y/%m/%d %I:%M:%S %p)").to_string(); let client_state = match client.state() { @@ -58,9 +62,9 @@ fn main() -> Result<(), Box> { spans.push(Span::raw(s)); } - Spans::from(spans) + Line::from(spans) } - ClientState::Connected => Spans::from(vec![ + ClientState::Connected => Line::from(vec![ Span::raw("state: "), Span::styled("connected", Style::default().fg(Color::Green)), ]), @@ -75,7 +79,7 @@ fn main() -> Result<(), Box> { let text = vec![ client_state, - Spans::from(vec![ + Line::from(vec![ Span::styled("controls: ", Style::default().add_modifier(Modifier::BOLD)), Span::raw("up/down = scroll, q = quit"), ]), @@ -149,21 +153,31 @@ fn main() -> Result<(), Box> { // Poll the event queue for input events. `next` will only block for 1 second, // so our screen is never stale by more than 1 second. - if let Some(input) = events.next()? { - match input { - Key::Char('q') => break, - Key::Up => selector.previous(), - Key::Down => selector.next(), - Key::PageUp => selector.top(), - Key::PageDown => selector.bottom(), + if let Some(input) = InputEvents::next()? { + match input.code { + KeyCode::Char('q') => break, + KeyCode::Up => selector.previous(), + KeyCode::Down => selector.next(), + KeyCode::PageUp => selector.top(), + KeyCode::PageDown => selector.bottom(), _ => {} } } } - Ok(()) } +fn init_terminal() -> io::Result>> { + enable_raw_mode()?; + execute!(io::stdout(), EnterAlternateScreen)?; + Terminal::new(CrosstermBackend::new(io::stdout())) +} + +fn restore_terminal() -> io::Result<()> { + disable_raw_mode()?; + execute!(io::stdout(), LeaveAlternateScreen) +} + fn u64_to_displayable(value: u64, unit: Option) -> String { let unit = match unit { None => return value.to_string(), @@ -211,7 +225,7 @@ fn f64_data_to_displayable(value: f64, unit: Unit) -> String { let offset = match unit { Unit::Kibibytes => 1, Unit::Mebibytes => 2, - Unit::Gigibytes => 3, + Unit::Gibibytes => 3, Unit::Tebibytes => 4, _ => 0, }; diff --git a/metrics-observer/src/selector.rs b/metrics-observer/src/selector.rs index 6b7a13a6..8c1a75df 100644 --- a/metrics-observer/src/selector.rs +++ b/metrics-observer/src/selector.rs @@ -1,4 +1,4 @@ -use tui::widgets::ListState; +use ratatui::widgets::ListState; pub struct Selector(usize, ListState); diff --git a/metrics-tracing-context/CHANGELOG.md b/metrics-tracing-context/CHANGELOG.md index 84d2247b..6dfe676d 100644 --- a/metrics-tracing-context/CHANGELOG.md +++ b/metrics-tracing-context/CHANGELOG.md @@ -8,6 +8,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] - ReleaseDate +### Added + +- Added `Debug` derive to numerous types. ([#504](https://github.com/metrics-rs/metrics/pull/504)) + +### Changed + +- Fixed a number of Clippy lints. ([#510](https://github.com/metrics-rs/metrics/pull/510)) + ## [0.16.0] - 2024-05-27 ### Changed diff --git a/metrics-tracing-context/benches/layer.rs b/metrics-tracing-context/benches/layer.rs index 892b6668..512e0f1a 100644 --- a/metrics-tracing-context/benches/layer.rs +++ b/metrics-tracing-context/benches/layer.rs @@ -12,9 +12,9 @@ fn layer_benchmark(c: &mut Criterion) { let mut group = c.benchmark_group("layer"); group.bench_function("base case", |b| { let recorder = NoopRecorder; - static KEY_NAME: &'static str = "key"; + static KEY_NAME: &str = "key"; static KEY_LABELS: [Label; 1] = [Label::from_static_parts("foo", "bar")]; - static KEY_DATA: Key = Key::from_static_parts(&KEY_NAME, &KEY_LABELS); + static KEY_DATA: Key = Key::from_static_parts(KEY_NAME, &KEY_LABELS); static METADATA: metrics::Metadata = metrics::Metadata::new(module_path!(), metrics::Level::INFO, Some(module_path!())); @@ -32,9 +32,9 @@ fn layer_benchmark(c: &mut Criterion) { let _guard = span.enter(); let recorder = NoopRecorder; - static KEY_NAME: &'static str = "key"; + static KEY_NAME: &str = "key"; static KEY_LABELS: [Label; 1] = [Label::from_static_parts("foo", "bar")]; - static KEY_DATA: Key = Key::from_static_parts(&KEY_NAME, &KEY_LABELS); + static KEY_DATA: Key = Key::from_static_parts(KEY_NAME, &KEY_LABELS); static METADATA: metrics::Metadata = metrics::Metadata::new(module_path!(), metrics::Level::INFO, Some(module_path!())); @@ -53,9 +53,9 @@ fn layer_benchmark(c: &mut Criterion) { let _guard = span.enter(); let recorder = NoopRecorder; - static KEY_NAME: &'static str = "key"; + static KEY_NAME: &str = "key"; static KEY_LABELS: [Label; 1] = [Label::from_static_parts("foo", "bar")]; - static KEY_DATA: Key = Key::from_static_parts(&KEY_NAME, &KEY_LABELS); + static KEY_DATA: Key = Key::from_static_parts(KEY_NAME, &KEY_LABELS); static METADATA: metrics::Metadata = metrics::Metadata::new(module_path!(), metrics::Level::INFO, Some(module_path!())); @@ -75,9 +75,9 @@ fn layer_benchmark(c: &mut Criterion) { let tracing_layer = TracingContextLayer::all(); let recorder = tracing_layer.layer(NoopRecorder); - static KEY_NAME: &'static str = "key"; + static KEY_NAME: &str = "key"; static KEY_LABELS: [Label; 1] = [Label::from_static_parts("foo", "bar")]; - static KEY_DATA: Key = Key::from_static_parts(&KEY_NAME, &KEY_LABELS); + static KEY_DATA: Key = Key::from_static_parts(KEY_NAME, &KEY_LABELS); static METADATA: metrics::Metadata = metrics::Metadata::new(module_path!(), metrics::Level::INFO, Some(module_path!())); @@ -97,9 +97,9 @@ fn layer_benchmark(c: &mut Criterion) { let tracing_layer = TracingContextLayer::all(); let recorder = tracing_layer.layer(NoopRecorder); - static KEY_NAME: &'static str = "key"; + static KEY_NAME: &str = "key"; static KEY_LABELS: [Label; 1] = [Label::from_static_parts("foo", "bar")]; - static KEY_DATA: Key = Key::from_static_parts(&KEY_NAME, &KEY_LABELS); + static KEY_DATA: Key = Key::from_static_parts(KEY_NAME, &KEY_LABELS); static METADATA: metrics::Metadata = metrics::Metadata::new(module_path!(), metrics::Level::INFO, Some(module_path!())); diff --git a/metrics-tracing-context/benches/visit.rs b/metrics-tracing-context/benches/visit.rs index 9b6db089..4e54217b 100644 --- a/metrics-tracing-context/benches/visit.rs +++ b/metrics-tracing-context/benches/visit.rs @@ -140,7 +140,7 @@ struct DebugStruct { impl DebugStruct { pub fn new() -> DebugStruct { - DebugStruct { field1: format!("yeehaw!"), field2: 324242343243 } + DebugStruct { field1: "yeehaw!".to_string(), field2: 324242343243 } } } diff --git a/metrics-tracing-context/src/lib.rs b/metrics-tracing-context/src/lib.rs index 5ea0efc6..b68d4c1b 100644 --- a/metrics-tracing-context/src/lib.rs +++ b/metrics-tracing-context/src/lib.rs @@ -114,6 +114,7 @@ use tracing_integration::Map; pub use tracing_integration::{Labels, MetricsLayer}; /// [`TracingContextLayer`] provides an implementation of a [`Layer`] for [`TracingContext`]. +#[derive(Debug)] pub struct TracingContextLayer { label_filter: F, } @@ -156,6 +157,7 @@ where } /// [`TracingContext`] is a [`metrics::Recorder`] that injects labels from [`tracing::Span`]s. +#[derive(Debug)] pub struct TracingContext { inner: R, label_filter: F, diff --git a/metrics-tracing-context/src/tracing_integration.rs b/metrics-tracing-context/src/tracing_integration.rs index d6e235f4..70fa2810 100644 --- a/metrics-tracing-context/src/tracing_integration.rs +++ b/metrics-tracing-context/src/tracing_integration.rs @@ -97,6 +97,7 @@ impl AsRef for Labels { /// fields and allows them to be later on used as metrics labels. #[derive(Default)] pub struct MetricsLayer { + #[allow(clippy::type_complexity)] with_labels: Option Option) -> Option>, } diff --git a/metrics-tracing-context/tests/integration.rs b/metrics-tracing-context/tests/integration.rs index ccceed30..be96a204 100644 --- a/metrics-tracing-context/tests/integration.rs +++ b/metrics-tracing-context/tests/integration.rs @@ -7,13 +7,13 @@ use tracing::dispatcher::{set_default, Dispatch}; use tracing::{span, Level}; use tracing_subscriber::{layer::SubscriberExt, Registry}; -static LOGIN_ATTEMPTS: &'static str = "login_attempts"; -static LOGIN_ATTEMPTS_NONE: &'static str = "login_attempts_no_labels"; -static LOGIN_ATTEMPTS_STATIC: &'static str = "login_attempts_static_labels"; -static LOGIN_ATTEMPTS_DYNAMIC: &'static str = "login_attempts_dynamic_labels"; -static LOGIN_ATTEMPTS_BOTH: &'static str = "login_attempts_static_and_dynamic_labels"; -static MY_COUNTER: &'static str = "my_counter"; -static USER_EMAIL: &'static [Label] = &[ +static LOGIN_ATTEMPTS: &str = "login_attempts"; +static LOGIN_ATTEMPTS_NONE: &str = "login_attempts_no_labels"; +static LOGIN_ATTEMPTS_STATIC: &str = "login_attempts_static_labels"; +static LOGIN_ATTEMPTS_DYNAMIC: &str = "login_attempts_dynamic_labels"; +static LOGIN_ATTEMPTS_BOTH: &str = "login_attempts_static_and_dynamic_labels"; +static MY_COUNTER: &str = "my_counter"; +static USER_EMAIL: &[Label] = &[ Label::from_static_parts("user", "ferris"), Label::from_static_parts("user.email", "ferris@rust-lang.org"), ]; @@ -522,7 +522,7 @@ fn test_nested_spans() { ); } -#[derive(Clone)] +#[derive(Clone, Debug)] struct OnlyUser; impl LabelFilter for OnlyUser { @@ -560,7 +560,7 @@ fn test_label_filtering() { #[test] fn test_label_allowlist() { - let snapshot = with_tracing_layer(TracingContextLayer::only_allow(&["env", "service"]), || { + let snapshot = with_tracing_layer(TracingContextLayer::only_allow(["env", "service"]), || { let user = "ferris"; let email = "ferris@rust-lang.org"; let span = span!( diff --git a/metrics-util/CHANGELOG.md b/metrics-util/CHANGELOG.md index e9f80d21..94f5180e 100644 --- a/metrics-util/CHANGELOG.md +++ b/metrics-util/CHANGELOG.md @@ -9,6 +9,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] - ReleaseDate +### Added + +- Added `Debug` derive to numerous types. ([#504](https://github.com/metrics-rs/metrics/pull/504)) + +### Changed + +- Replaced `num_cpus::get` with `std::thread::available_parallelism`. + ([#500](https://github.com/metrics-rs/metrics/pull/500)) +- Fixed a number of Clippy lints. ([#510](https://github.com/metrics-rs/metrics/pull/510)) +- Added `Sync` constraint to generic parameter in `RecoverableRecorder` and `Stack`. ([#511](https://github.com/metrics-rs/metrics/pull/511)) + ## [0.17.0] - 2024-05-27 ### Changed diff --git a/metrics-util/Cargo.toml b/metrics-util/Cargo.toml index 2dc71a5e..5df68559 100644 --- a/metrics-util/Cargo.toml +++ b/metrics-util/Cargo.toml @@ -56,7 +56,6 @@ quanta = { version = "0.12", default-features = false, optional = true } sketches-ddsketch = { version = "0.2", default-features = false, optional = true } radix_trie = { version = "0.2", default-features = false, optional = true } ordered-float = { version = "4.2", default-features = false, optional = true } -num_cpus = { version = "1", default-features = false, optional = true } ahash = { version = "0.8.8", default-features = false, optional = true } hashbrown = { version = "0.14", default-features = false, optional = true, features = ["ahash"] } @@ -90,4 +89,4 @@ layer-filter = ["aho-corasick"] layer-router = ["radix_trie"] summary = ["sketches-ddsketch"] recency = ["registry", "quanta"] -registry = ["crossbeam-epoch", "crossbeam-utils", "handles", "hashbrown", "num_cpus"] +registry = ["crossbeam-epoch", "crossbeam-utils", "handles", "hashbrown"] diff --git a/metrics-util/benches/filter.rs b/metrics-util/benches/filter.rs index 402b24be..40f4e5c5 100644 --- a/metrics-util/benches/filter.rs +++ b/metrics-util/benches/filter.rs @@ -15,9 +15,9 @@ fn layer_benchmark(c: &mut Criterion) { let patterns = vec!["tokio"]; let filter_layer = FilterLayer::from_patterns(patterns); let recorder = filter_layer.layer(NoopRecorder); - static KEY_NAME: &'static str = "tokio.foo"; + static KEY_NAME: &str = "tokio.foo"; static KEY_LABELS: [Label; 1] = [Label::from_static_parts("foo", "bar")]; - static KEY_DATA: Key = Key::from_static_parts(&KEY_NAME, &KEY_LABELS); + static KEY_DATA: Key = Key::from_static_parts(KEY_NAME, &KEY_LABELS); static METADATA: metrics::Metadata = metrics::Metadata::new(module_path!(), metrics::Level::INFO, Some(module_path!())); @@ -29,9 +29,9 @@ fn layer_benchmark(c: &mut Criterion) { let patterns = vec!["tokio"]; let filter_layer = FilterLayer::from_patterns(patterns); let recorder = filter_layer.layer(NoopRecorder); - static KEY_NAME: &'static str = "hyper.foo"; + static KEY_NAME: &str = "hyper.foo"; static KEY_LABELS: [Label; 1] = [Label::from_static_parts("foo", "bar")]; - static KEY_DATA: Key = Key::from_static_parts(&KEY_NAME, &KEY_LABELS); + static KEY_DATA: Key = Key::from_static_parts(KEY_NAME, &KEY_LABELS); static METADATA: metrics::Metadata = metrics::Metadata::new(module_path!(), metrics::Level::INFO, Some(module_path!())); @@ -41,9 +41,9 @@ fn layer_benchmark(c: &mut Criterion) { }); group.bench_function("noop recorder overhead (increment_counter)", |b| { let recorder = NoopRecorder; - static KEY_NAME: &'static str = "tokio.foo"; + static KEY_NAME: &str = "tokio.foo"; static KEY_LABELS: [Label; 1] = [Label::from_static_parts("foo", "bar")]; - static KEY_DATA: Key = Key::from_static_parts(&KEY_NAME, &KEY_LABELS); + static KEY_DATA: Key = Key::from_static_parts(KEY_NAME, &KEY_LABELS); static METADATA: metrics::Metadata = metrics::Metadata::new(module_path!(), metrics::Level::INFO, Some(module_path!())); diff --git a/metrics-util/benches/prefix.rs b/metrics-util/benches/prefix.rs index ce2beb3a..54bf964a 100644 --- a/metrics-util/benches/prefix.rs +++ b/metrics-util/benches/prefix.rs @@ -7,9 +7,9 @@ fn layer_benchmark(c: &mut Criterion) { group.bench_function("basic", |b| { let prefix_layer = PrefixLayer::new("prefix"); let recorder = prefix_layer.layer(NoopRecorder); - static KEY_NAME: &'static str = "simple_key"; + static KEY_NAME: &str = "simple_key"; static KEY_LABELS: [Label; 1] = [Label::from_static_parts("foo", "bar")]; - static KEY_DATA: Key = Key::from_static_parts(&KEY_NAME, &KEY_LABELS); + static KEY_DATA: Key = Key::from_static_parts(KEY_NAME, &KEY_LABELS); static METADATA: metrics::Metadata = metrics::Metadata::new(module_path!(), metrics::Level::INFO, Some(module_path!())); @@ -19,9 +19,9 @@ fn layer_benchmark(c: &mut Criterion) { }); group.bench_function("noop recorder overhead (increment_counter)", |b| { let recorder = NoopRecorder; - static KEY_NAME: &'static str = "simple_key"; + static KEY_NAME: &str = "simple_key"; static KEY_LABELS: [Label; 1] = [Label::from_static_parts("foo", "bar")]; - static KEY_DATA: Key = Key::from_static_parts(&KEY_NAME, &KEY_LABELS); + static KEY_DATA: Key = Key::from_static_parts(KEY_NAME, &KEY_LABELS); static METADATA: metrics::Metadata = metrics::Metadata::new(module_path!(), metrics::Level::INFO, Some(module_path!())); diff --git a/metrics-util/benches/registry.rs b/metrics-util/benches/registry.rs index 916440ff..80e386c9 100644 --- a/metrics-util/benches/registry.rs +++ b/metrics-util/benches/registry.rs @@ -6,22 +6,22 @@ fn registry_benchmark(c: &mut Criterion) { let mut group = c.benchmark_group("registry"); group.bench_function("cached op (basic)", |b| { let registry = Registry::atomic(); - static KEY_NAME: &'static str = "simple_key"; - static KEY_DATA: Key = Key::from_static_name(&KEY_NAME); + static KEY_NAME: &str = "simple_key"; + static KEY_DATA: Key = Key::from_static_name(KEY_NAME); b.iter(|| registry.get_or_create_counter(&KEY_DATA, |_| ())) }); group.bench_function("cached op (labels)", |b| { let registry = Registry::atomic(); - static KEY_NAME: &'static str = "simple_key"; + static KEY_NAME: &str = "simple_key"; static KEY_LABELS: [Label; 1] = [Label::from_static_parts("type", "http")]; - static KEY_DATA: Key = Key::from_static_parts(&KEY_NAME, &KEY_LABELS); + static KEY_DATA: Key = Key::from_static_parts(KEY_NAME, &KEY_LABELS); b.iter(|| registry.get_or_create_counter(&KEY_DATA, |_| ())) }); group.bench_function("uncached op (basic)", |b| { b.iter_batched_ref( - || Registry::atomic(), + Registry::atomic, |registry| { let key = "simple_key".into(); registry.get_or_create_counter(&key, |_| ()) @@ -31,7 +31,7 @@ fn registry_benchmark(c: &mut Criterion) { }); group.bench_function("uncached op (labels)", |b| { b.iter_batched_ref( - || Registry::atomic(), + Registry::atomic, |registry| { let labels = vec![Label::new("type", "http")]; let key = ("simple_key", labels).into(); @@ -45,15 +45,15 @@ fn registry_benchmark(c: &mut Criterion) { }); group.bench_function("const key overhead (basic)", |b| { b.iter(|| { - static KEY_NAME: &'static str = "simple_key"; - Key::from_static_name(&KEY_NAME) + static KEY_NAME: &str = "simple_key"; + Key::from_static_name(KEY_NAME) }) }); group.bench_function("const key data overhead (labels)", |b| { b.iter(|| { - static KEY_NAME: &'static str = "simple_key"; + static KEY_NAME: &str = "simple_key"; static LABELS: [Label; 1] = [Label::from_static_parts("type", "http")]; - Key::from_static_parts(&KEY_NAME, &LABELS) + Key::from_static_parts(KEY_NAME, &LABELS) }) }); group.bench_function("owned key overhead (basic)", |b| b.iter(|| Key::from_name("simple_key"))); diff --git a/metrics-util/src/debugging.rs b/metrics-util/src/debugging.rs index d17a222f..2e5650ea 100644 --- a/metrics-util/src/debugging.rs +++ b/metrics-util/src/debugging.rs @@ -36,6 +36,7 @@ impl CompositeKeyName { } /// A point-in-time snapshot of all metrics in [`DebuggingRecorder`]. +#[derive(Debug)] pub struct Snapshot(Vec<(CompositeKey, Option, Option, DebugValue)>); impl Snapshot { @@ -67,6 +68,7 @@ pub enum DebugValue { Histogram(Vec>), } +#[derive(Debug)] struct Inner { registry: Registry, seen: Mutex>, @@ -84,7 +86,7 @@ impl Inner { } /// Captures point-in-time snapshots of [`DebuggingRecorder`]. -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Snapshotter { inner: Arc, } @@ -138,6 +140,7 @@ impl Snapshotter { /// /// Callers can easily take snapshots of the metrics at any given time and get access /// to the raw values. +#[derive(Debug)] pub struct DebuggingRecorder { inner: Arc, } diff --git a/metrics-util/src/layers/fanout.rs b/metrics-util/src/layers/fanout.rs index 430b61d6..60c67227 100644 --- a/metrics-util/src/layers/fanout.rs +++ b/metrics-util/src/layers/fanout.rs @@ -1,10 +1,11 @@ -use std::sync::Arc; +use std::{fmt, sync::Arc}; use metrics::{ Counter, CounterFn, Gauge, GaugeFn, Histogram, HistogramFn, Key, KeyName, Metadata, Recorder, SharedString, Unit, }; +#[derive(Debug)] struct FanoutCounter { counters: Vec, } @@ -35,6 +36,7 @@ impl From for Counter { } } +#[derive(Debug)] struct FanoutGauge { gauges: Vec, } @@ -71,6 +73,7 @@ impl From for Gauge { } } +#[derive(Debug)] struct FanoutHistogram { histograms: Vec, } @@ -100,6 +103,14 @@ pub struct Fanout { recorders: Vec>, } +impl fmt::Debug for Fanout { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Fanout") + .field("recorders_len", &self.recorders.len()) + .finish_non_exhaustive() + } +} + impl Recorder for Fanout { fn describe_counter(&self, key_name: KeyName, unit: Option, description: SharedString) { for recorder in &self.recorders { @@ -155,6 +166,14 @@ pub struct FanoutBuilder { recorders: Vec>, } +impl fmt::Debug for FanoutBuilder { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FanoutBuilder") + .field("recorders_len", &self.recorders.len()) + .finish_non_exhaustive() + } +} + impl FanoutBuilder { /// Adds a recorder to the fanout list. pub fn add_recorder(mut self, recorder: R) -> FanoutBuilder diff --git a/metrics-util/src/layers/filter.rs b/metrics-util/src/layers/filter.rs index 4d0db28b..e9b96802 100644 --- a/metrics-util/src/layers/filter.rs +++ b/metrics-util/src/layers/filter.rs @@ -5,6 +5,7 @@ use metrics::{Counter, Gauge, Histogram, Key, KeyName, Metadata, Recorder, Share /// Filters and discards metrics matching certain name patterns. /// /// More information on the behavior of the layer can be found in [`FilterLayer`]. +#[derive(Debug)] pub struct Filter { inner: R, automaton: AhoCorasick, @@ -73,7 +74,7 @@ impl Recorder for Filter { /// DFA, or case sensitivity. /// /// [ahocorasick]: https://en.wikipedia.org/wiki/Aho–Corasick_algorithm -#[derive(Default)] +#[derive(Default, Debug)] pub struct FilterLayer { patterns: Vec, case_insensitive: bool, @@ -223,7 +224,7 @@ mod tests { ]; let recorder = MockBasicRecorder::from_operations(expectations); - let filter = FilterLayer::from_patterns(&["tokio", "bb8"]); + let filter = FilterLayer::from_patterns(["tokio", "bb8"]); let filter = filter.layer(recorder); for operation in inputs { @@ -294,7 +295,7 @@ mod tests { ]; let recorder = MockBasicRecorder::from_operations(expectations); - let mut filter = FilterLayer::from_patterns(&["tokio", "bb8"]); + let mut filter = FilterLayer::from_patterns(["tokio", "bb8"]); let filter = filter.case_insensitive(true).layer(recorder); for operation in inputs { diff --git a/metrics-util/src/layers/mod.rs b/metrics-util/src/layers/mod.rs index 388a0278..d58f598a 100644 --- a/metrics-util/src/layers/mod.rs +++ b/metrics-util/src/layers/mod.rs @@ -12,7 +12,7 @@ //! # use metrics::NoopRecorder as BasicRecorder; //! # use metrics_util::layers::{Layer, Stack, PrefixLayer}; //! // A simple layer that denies any metrics that have "stairway" or "heaven" in their name. -//! #[derive(Default)] +//! #[derive(Default, Debug)] //! pub struct StairwayDeny(pub(crate) R); //! //! impl StairwayDeny { @@ -75,7 +75,7 @@ //! } //! } //! -//! #[derive(Default)] +//! #[derive(Debug, Default)] //! pub struct StairwayDenyLayer; //! //! impl Layer for StairwayDenyLayer { @@ -137,6 +137,7 @@ pub trait Layer { } /// Builder for composing layers together in a top-down/inside-out order. +#[derive(Debug)] pub struct Stack { inner: R, } diff --git a/metrics-util/src/layers/prefix.rs b/metrics-util/src/layers/prefix.rs index 6c4f3829..58d5156f 100644 --- a/metrics-util/src/layers/prefix.rs +++ b/metrics-util/src/layers/prefix.rs @@ -4,6 +4,7 @@ use metrics::{Counter, Gauge, Histogram, Key, KeyName, Metadata, Recorder, Share /// Applies a prefix to every metric key. /// /// Keys will be prefixed in the format of `.`. +#[derive(Debug)] pub struct Prefix { prefix: SharedString, inner: R, @@ -64,6 +65,7 @@ impl Recorder for Prefix { /// A layer for applying a prefix to every metric key. /// /// More information on the behavior of the layer can be found in [`Prefix`]. +#[derive(Debug)] pub struct PrefixLayer(&'static str); impl PrefixLayer { diff --git a/metrics-util/src/layers/router.rs b/metrics-util/src/layers/router.rs index f0a7dbea..448ae5da 100644 --- a/metrics-util/src/layers/router.rs +++ b/metrics-util/src/layers/router.rs @@ -1,3 +1,5 @@ +use std::fmt; + use metrics::{Counter, Gauge, Histogram, Key, KeyName, Metadata, Recorder, SharedString, Unit}; use radix_trie::{Trie, TrieCommon}; @@ -15,6 +17,17 @@ pub struct Router { histogram_routes: Trie, } +impl fmt::Debug for Router { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Router") + .field("global_mask", &self.global_mask) + .field("targets_len", &self.targets.len()) + .field("counter_routes", &self.counter_routes) + .field("gauge_routes", &self.gauge_routes) + .field("histogram_routes", &self.histogram_routes) + .finish_non_exhaustive() + } +} impl Router { fn route( &self, @@ -87,6 +100,18 @@ pub struct RouterBuilder { histogram_routes: Trie, } +impl fmt::Debug for RouterBuilder { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RouterBuilder") + .field("global_mask", &self.global_mask) + .field("targets_len", &self.targets.len()) + .field("counter_routes", &self.counter_routes) + .field("gauge_routes", &self.gauge_routes) + .field("histogram_routes", &self.histogram_routes) + .finish_non_exhaustive() + } +} + impl RouterBuilder { /// Creates a [`RouterBuilder`] from a [`Recorder`]. /// @@ -175,6 +200,7 @@ mod tests { }; mock! { + #[derive(Debug)] pub TestRecorder { } @@ -193,9 +219,10 @@ mod tests { let _ = RouterBuilder::from_recorder(MockTestRecorder::new()).build(); let mut builder = RouterBuilder::from_recorder(MockTestRecorder::new()); + // ensure that &str, String, and Cow are all are accepted by the builder builder .add_route(MetricKindMask::COUNTER, "foo", MockTestRecorder::new()) - .add_route(MetricKindMask::GAUGE, "bar".to_owned(), MockTestRecorder::new()) + .add_route(MetricKindMask::GAUGE, String::from("bar"), MockTestRecorder::new()) .add_route(MetricKindMask::HISTOGRAM, Cow::Borrowed("baz"), MockTestRecorder::new()) .add_route(MetricKindMask::ALL, "quux", MockTestRecorder::new()); let _ = builder.build(); diff --git a/metrics-util/src/recoverable.rs b/metrics-util/src/recoverable.rs index 46df3770..7f9a3b04 100644 --- a/metrics-util/src/recoverable.rs +++ b/metrics-util/src/recoverable.rs @@ -5,6 +5,7 @@ use metrics::{ Unit, }; +#[derive(Debug)] pub struct RecoveryHandle { handle: Arc, } @@ -51,6 +52,7 @@ impl RecoveryHandle { /// This allows using `RecoveryHandle` as a drop guard, ensuring that by dropping it, the /// recorder itself will be dropped, and any finalization logic implemented for the recorder will be /// run. +#[derive(Debug)] pub struct RecoverableRecorder { handle: Arc, } @@ -88,6 +90,7 @@ impl RecoverableRecorder { } } +#[derive(Debug)] struct WeakRecorder { recorder: Weak, } @@ -149,8 +152,13 @@ mod tests { use super::*; use metrics::{atomics::AtomicU64, CounterFn, GaugeFn, HistogramFn, Key, Recorder}; + #[derive(Debug)] struct CounterWrapper(AtomicU64); + + #[derive(Debug)] struct GaugeWrapper(AtomicU64); + + #[derive(Debug)] struct HistogramWrapper(AtomicU64); impl CounterWrapper { @@ -201,6 +209,7 @@ mod tests { } } + #[derive(Debug)] struct TestRecorder { dropped: Arc, counter: Arc, diff --git a/metrics-util/src/registry/mod.rs b/metrics-util/src/registry/mod.rs index a70e65f8..d7375b49 100644 --- a/metrics-util/src/registry/mod.rs +++ b/metrics-util/src/registry/mod.rs @@ -45,6 +45,7 @@ type RegistryHashMap = HashMap>; /// ## Performance /// /// `Registry` is optimized for reads. +#[derive(Debug)] pub struct Registry where S: Storage, @@ -56,10 +57,14 @@ where storage: S, } +fn shard_count() -> usize { + std::thread::available_parallelism().map(|x| x.get()).unwrap_or(1).next_power_of_two() +} + impl Registry { /// Creates a new `Registry` using a regular [`Key`] and atomic storage. pub fn atomic() -> Self { - let shard_count = std::cmp::max(1, num_cpus::get()).next_power_of_two(); + let shard_count = shard_count(); let shard_mask = shard_count - 1; let counters = repeat(()).take(shard_count).map(|_| RwLock::new(RegistryHashMap::default())).collect(); @@ -78,7 +83,7 @@ where { /// Creates a new `Registry`. pub fn new(storage: S) -> Self { - let shard_count = std::cmp::max(1, num_cpus::get()).next_power_of_two(); + let shard_count = shard_count(); let shard_mask = shard_count - 1; let counters = repeat(()).take(shard_count).map(|_| RwLock::new(RegistryHashMap::default())).collect(); diff --git a/metrics-util/src/registry/recency.rs b/metrics-util/src/registry/recency.rs index 0ccf014a..1c0bcca6 100644 --- a/metrics-util/src/registry/recency.rs +++ b/metrics-util/src/registry/recency.rs @@ -54,7 +54,7 @@ pub struct Generation(usize); /// again at a later point in time, it could have changed in between the two observations. It also /// may not have changed, and thus `Generational` provides a way to determine if either of these /// events occurred. -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Generational { inner: T, gen: Arc, @@ -157,6 +157,7 @@ where /// /// Tracks the "generation" of a metric, which is used to detect updates to metrics where the value /// otherwise would not be sufficient to be used as an indicator. +#[derive(Debug)] pub struct GenerationalStorage { inner: S, } @@ -215,8 +216,10 @@ impl GenerationalAtomicStorage { /// /// [`Recency`] is separate from [`Registry`] specifically to avoid imposing any slowdowns when /// tracking recency does not matter, despite their otherwise tight coupling. +#[derive(Debug)] pub struct Recency { mask: MetricKindMask, + #[allow(clippy::type_complexity)] inner: Mutex<(Clock, HashMap)>, idle_timeout: Option, } diff --git a/metrics-util/src/registry/storage.rs b/metrics-util/src/registry/storage.rs index 0e61cf9a..0e6ca0a5 100644 --- a/metrics-util/src/registry/storage.rs +++ b/metrics-util/src/registry/storage.rs @@ -29,6 +29,7 @@ pub trait Storage { /// /// Utilizes atomics for storing the value(s) of a given metric. Shared access to the actual atomic /// is handling via `Arc`. +#[derive(Debug)] pub struct AtomicStorage; impl Storage for AtomicStorage { diff --git a/metrics-util/src/summary.rs b/metrics-util/src/summary.rs index cd4c5cc2..4e65e104 100644 --- a/metrics-util/src/summary.rs +++ b/metrics-util/src/summary.rs @@ -45,6 +45,13 @@ pub struct Summary { sketch: DDSketch, } +impl fmt::Debug for Summary { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // manual implementation because DDSketch does not implement Debug + f.debug_struct("Summary").finish_non_exhaustive() + } +} + impl Summary { /// Creates a new [`Summary`]. /// diff --git a/metrics-util/src/test_util.rs b/metrics-util/src/test_util.rs index cae3fc1d..455ff165 100644 --- a/metrics-util/src/test_util.rs +++ b/metrics-util/src/test_util.rs @@ -5,7 +5,7 @@ use mockall::{ Predicate, }; -#[derive(Clone)] +#[derive(Clone, Debug)] pub enum RecorderOperation { DescribeCounter(KeyName, Option, SharedString), DescribeGauge(KeyName, Option, SharedString), @@ -67,6 +67,7 @@ impl RecorderOperation { } mock! { + #[derive(Debug)] pub BasicRecorder {} impl Recorder for BasicRecorder { diff --git a/metrics/CHANGELOG.md b/metrics/CHANGELOG.md index c1e882f7..c3fa5367 100644 --- a/metrics/CHANGELOG.md +++ b/metrics/CHANGELOG.md @@ -10,8 +10,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Added `Debug` derive to numerous types. ([#504](https://github.com/metrics-rs/metrics/pull/504)) - Blanket implementations of `Recorder` over smart pointer representations (i.e. `Arc where T: Recorder`). ([#512](https://github.com/metrics-rs/metrics/pull/512)) +### Changed + +- Changed `Unit::Gigibytes` to `Gibibytes` to match the proper SI prefix. + ([#508](https://github.com/metrics-rs/metrics/pull/508)) +- Fixed a number of Clippy lints. ([#510](https://github.com/metrics-rs/metrics/pull/510)) +- Updated the documentation for `with_local_recorder` to better explain limitations. +- `set_global_recorder` now requires that the recorder is `Sync`. ([#511](https://github.com/metrics-rs/metrics/pull/511)) + ## [0.23.0] - 2024-05-27 ### Added diff --git a/metrics/benches/macros.rs b/metrics/benches/macros.rs index 291d143f..7394d2c8 100644 --- a/metrics/benches/macros.rs +++ b/metrics/benches/macros.rs @@ -8,8 +8,9 @@ use metrics::{ }; use rand::{thread_rng, Rng}; -#[derive(Default)] +#[derive(Debug)] struct TestRecorder; + impl Recorder for TestRecorder { fn describe_counter(&self, _: KeyName, _: Option, _: SharedString) {} fn describe_gauge(&self, _: KeyName, _: Option, _: SharedString) {} @@ -38,19 +39,19 @@ fn macro_benchmark(c: &mut Criterion) { }) }); group.bench_function("global_initialized/no_labels", |b| { - let _ = metrics::set_global_recorder(TestRecorder::default()); + let _ = metrics::set_global_recorder(TestRecorder); b.iter(|| { counter!("counter_bench").increment(42); }); }); group.bench_function("global_initialized/with_static_labels", |b| { - let _ = metrics::set_global_recorder(TestRecorder::default()); + let _ = metrics::set_global_recorder(TestRecorder); b.iter(|| { counter!("counter_bench", "request" => "http", "svc" => "admin").increment(42); }); }); group.bench_function("global_initialized/with_dynamic_labels", |b| { - let _ = metrics::set_global_recorder(TestRecorder::default()); + let _ = metrics::set_global_recorder(TestRecorder); let label_val = thread_rng().gen::().to_string(); b.iter(move || { @@ -59,27 +60,21 @@ fn macro_benchmark(c: &mut Criterion) { }); }); group.bench_function("local_initialized/no_labels", |b| { - let recorder = TestRecorder::default(); - - metrics::with_local_recorder(&recorder, || { + metrics::with_local_recorder(&TestRecorder, || { b.iter(|| { counter!("counter_bench").increment(42); }); }); }); group.bench_function("local_initialized/with_static_labels", |b| { - let recorder = TestRecorder::default(); - - metrics::with_local_recorder(&recorder, || { + metrics::with_local_recorder(&TestRecorder, || { b.iter(|| { counter!("counter_bench", "request" => "http", "svc" => "admin").increment(42); }); }); }); group.bench_function("local_initialized/with_dynamic_labels", |b| { - let recorder = TestRecorder::default(); - - metrics::with_local_recorder(&recorder, || { + metrics::with_local_recorder(&TestRecorder, || { let label_val = thread_rng().gen::().to_string(); b.iter(move || { counter!("counter_bench", "request" => "http", "uid" => label_val.clone()) diff --git a/metrics/examples/basic.rs b/metrics/examples/basic.rs index 8d638386..bd190715 100644 --- a/metrics/examples/basic.rs +++ b/metrics/examples/basic.rs @@ -13,6 +13,7 @@ use metrics::{ }; use metrics::{Counter, CounterFn, Gauge, GaugeFn, Histogram, HistogramFn, Key, Recorder, Unit}; +#[derive(Clone, Debug)] struct PrintHandle(Key); impl CounterFn for PrintHandle { @@ -45,7 +46,7 @@ impl HistogramFn for PrintHandle { } } -#[derive(Default)] +#[derive(Debug)] struct PrintRecorder; impl Recorder for PrintRecorder { @@ -90,8 +91,7 @@ impl Recorder for PrintRecorder { } fn init_print_logger() { - let recorder = PrintRecorder::default(); - metrics::set_global_recorder(recorder).unwrap() + metrics::set_global_recorder(PrintRecorder).unwrap() } fn main() { diff --git a/metrics/src/common.rs b/metrics/src/common.rs index bfd3856d..b97658d9 100644 --- a/metrics/src/common.rs +++ b/metrics/src/common.rs @@ -23,7 +23,7 @@ pub type SharedString = Cow<'static, str>; /// /// For any use-case within a `metrics`-owned or adjacent crate, where hashing of a key is required, /// this is the hasher that will be used. -#[derive(Default)] +#[derive(Debug, Default)] pub struct KeyHasher(AHasher); impl Hasher for KeyHasher { @@ -84,12 +84,12 @@ pub enum Unit { Nanoseconds, /// Tebibytes. /// - /// One tebibyte is equal to 1024 gigibytes. + /// One tebibyte is equal to 1024 gibibytes. Tebibytes, - /// Gigibytes. + /// Gibibytes. /// - /// One gigibyte is equal to 1024 mebibytes. - Gigibytes, + /// One gibibyte is equal to 1024 mebibytes. + Gibibytes, /// Mebibytes. /// /// One mebibyte is equal to 1024 kibibytes. @@ -133,7 +133,7 @@ impl Unit { Unit::Microseconds => "microseconds", Unit::Nanoseconds => "nanoseconds", Unit::Tebibytes => "tebibytes", - Unit::Gigibytes => "gigibytes", + Unit::Gibibytes => "gibibytes", Unit::Mebibytes => "mebibytes", Unit::Kibibytes => "kibibytes", Unit::Bytes => "bytes", @@ -161,7 +161,7 @@ impl Unit { Unit::Microseconds => "μs", Unit::Nanoseconds => "ns", Unit::Tebibytes => "TiB", - Unit::Gigibytes => "GiB", + Unit::Gibibytes => "GiB", Unit::Mebibytes => "MiB", Unit::Kibibytes => "KiB", Unit::Bytes => "B", @@ -186,7 +186,7 @@ impl Unit { "microseconds" => Some(Unit::Microseconds), "nanoseconds" => Some(Unit::Nanoseconds), "tebibytes" => Some(Unit::Tebibytes), - "gigibytes" => Some(Unit::Gigibytes), + "gibibytes" => Some(Unit::Gibibytes), "mebibytes" => Some(Unit::Mebibytes), "kibibytes" => Some(Unit::Kibibytes), "bytes" => Some(Unit::Bytes), @@ -210,7 +210,7 @@ impl Unit { matches!( self, Unit::Tebibytes - | Unit::Gigibytes + | Unit::Gibibytes | Unit::Mebibytes | Unit::Kibibytes | Unit::Bytes @@ -276,7 +276,7 @@ macro_rules! into_f64 { }; } -pub(self) use into_f64; +use into_f64; #[cfg(test)] mod tests { @@ -294,7 +294,7 @@ mod tests { Unit::Microseconds, Unit::Nanoseconds, Unit::Tebibytes, - Unit::Gigibytes, + Unit::Gibibytes, Unit::Mebibytes, Unit::Kibibytes, Unit::Bytes, diff --git a/metrics/src/cow.rs b/metrics/src/cow.rs index 2338c220..c510673c 100644 --- a/metrics/src/cow.rs +++ b/metrics/src/cow.rs @@ -412,7 +412,7 @@ unsafe impl Sync for Cow<'_, T> {} unsafe impl Send for Cow<'_, T> {} #[repr(C)] -#[derive(Clone, Copy, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Metadata(usize, usize); impl Metadata { diff --git a/metrics/src/handles.rs b/metrics/src/handles.rs index 33dc62d2..ccc7e6ac 100644 --- a/metrics/src/handles.rs +++ b/metrics/src/handles.rs @@ -1,4 +1,4 @@ -use std::sync::Arc; +use std::{fmt::Debug, sync::Arc}; use crate::IntoF64; @@ -45,6 +45,12 @@ pub struct Counter { inner: Option>, } +impl Debug for Counter { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Counter").finish_non_exhaustive() + } +} + /// A gauge. #[derive(Clone)] #[must_use = "gauges do nothing unless you use them"] @@ -52,6 +58,12 @@ pub struct Gauge { inner: Option>, } +impl Debug for Gauge { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Gauge").finish_non_exhaustive() + } +} + /// A histogram. #[derive(Clone)] #[must_use = "histograms do nothing unless you use them"] @@ -59,6 +71,12 @@ pub struct Histogram { inner: Option>, } +impl Debug for Histogram { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Histogram").finish_non_exhaustive() + } +} + impl Counter { /// Creates a no-op `Counter` which does nothing. /// diff --git a/metrics/src/key.rs b/metrics/src/key.rs index 900a7bf9..881fce29 100644 --- a/metrics/src/key.rs +++ b/metrics/src/key.rs @@ -260,25 +260,19 @@ mod tests { use crate::{KeyName, Label}; use std::{collections::HashMap, ops::Deref, sync::Arc}; - static BORROWED_NAME: &'static str = "name"; - static FOOBAR_NAME: &'static str = "foobar"; - static BORROWED_BASIC: Key = Key::from_static_name(&BORROWED_NAME); + static BORROWED_NAME: &str = "name"; + static FOOBAR_NAME: &str = "foobar"; + static BORROWED_BASIC: Key = Key::from_static_name(BORROWED_NAME); static LABELS: [Label; 1] = [Label::from_static_parts("key", "value")]; - static BORROWED_LABELS: Key = Key::from_static_parts(&BORROWED_NAME, &LABELS); + static BORROWED_LABELS: Key = Key::from_static_parts(BORROWED_NAME, &LABELS); #[test] fn test_key_ord_and_partialord() { - let keys_expected: Vec = vec![ - Key::from_name("aaaa").into(), - Key::from_name("bbbb").into(), - Key::from_name("cccc").into(), - ]; - - let keys_unsorted: Vec = vec![ - Key::from_name("bbbb").into(), - Key::from_name("cccc").into(), - Key::from_name("aaaa").into(), - ]; + let keys_expected: Vec = + vec![Key::from_name("aaaa"), Key::from_name("bbbb"), Key::from_name("cccc")]; + + let keys_unsorted: Vec = + vec![Key::from_name("bbbb"), Key::from_name("cccc"), Key::from_name("aaaa")]; let keys = { let mut keys = keys_unsorted.clone(); @@ -299,7 +293,7 @@ mod tests { fn test_key_eq_and_hash() { let mut keys = HashMap::new(); - let owned_basic: Key = Key::from_name("name").into(); + let owned_basic: Key = Key::from_name("name"); assert_eq!(&owned_basic, &BORROWED_BASIC); let previous = keys.insert(owned_basic, 42); @@ -309,7 +303,7 @@ mod tests { assert_eq!(previous, Some(&42)); let labels = LABELS.to_vec(); - let owned_labels = Key::from_parts(&BORROWED_NAME[..], labels); + let owned_labels = Key::from_parts(BORROWED_NAME, labels); assert_eq!(&owned_labels, &BORROWED_LABELS); let previous = keys.insert(owned_labels, 43); @@ -329,19 +323,19 @@ mod tests { let result1 = key1.to_string(); assert_eq!(result1, "Key(foobar)"); - let key2 = Key::from_parts(&FOOBAR_NAME[..], vec![Label::new("system", "http")]); + let key2 = Key::from_parts(FOOBAR_NAME, vec![Label::new("system", "http")]); let result2 = key2.to_string(); assert_eq!(result2, "Key(foobar, [system = http])"); let key3 = Key::from_parts( - &FOOBAR_NAME[..], + FOOBAR_NAME, vec![Label::new("system", "http"), Label::new("user", "joe")], ); let result3 = key3.to_string(); assert_eq!(result3, "Key(foobar, [system = http, user = joe])"); let key4 = Key::from_parts( - &FOOBAR_NAME[..], + FOOBAR_NAME, vec![ Label::new("black", "black"), Label::new("lives", "lives"), @@ -354,7 +348,7 @@ mod tests { #[test] fn test_key_name_equality() { - static KEY_NAME: &'static str = "key_name"; + static KEY_NAME: &str = "key_name"; let borrowed_const = KeyName::from_const_str(KEY_NAME); let borrowed_nonconst = KeyName::from(KEY_NAME); diff --git a/metrics/src/recorder/cell.rs b/metrics/src/recorder/cell.rs index 5450b6f0..a13feb1f 100644 --- a/metrics/src/recorder/cell.rs +++ b/metrics/src/recorder/cell.rs @@ -14,6 +14,7 @@ const INITIALIZING: usize = 1; const INITIALIZED: usize = 2; /// An specialized version of `OnceCell` for `Recorder`. +#[derive(Debug)] pub struct RecorderOnceCell { recorder: UnsafeCell>, state: AtomicUsize, diff --git a/metrics/src/recorder/mod.rs b/metrics/src/recorder/mod.rs index 7832f90b..91125dcd 100644 --- a/metrics/src/recorder/mod.rs +++ b/metrics/src/recorder/mod.rs @@ -176,6 +176,10 @@ where } /// Runs the closure with the given recorder set as the global recorder for the duration. +/// +/// This only applies as long as the closure is running, and on the thread where `with_local_recorder` is called. This +/// does not extend to other threads, and so is not suitable for capturing metrics in asynchronous code where multiple +/// threads are involved. pub fn with_local_recorder(recorder: &dyn Recorder, f: impl FnOnce() -> T) -> T { let _local = LocalRecorderGuard::new(recorder); f() @@ -222,6 +226,7 @@ mod tests { // This test simply ensures that if a boxed recorder is handed to us to install, and another // recorder has already been installed, that we drop th new boxed recorder instead of // leaking it. + #[derive(Debug)] struct TrackOnDropRecorder(Arc); impl TrackOnDropRecorder { diff --git a/metrics/src/recorder/noop.rs b/metrics/src/recorder/noop.rs index 8c44fb6b..b7dbcfb0 100644 --- a/metrics/src/recorder/noop.rs +++ b/metrics/src/recorder/noop.rs @@ -4,6 +4,7 @@ use crate::{Counter, Gauge, Histogram, Key, KeyName, Metadata, Recorder, SharedS /// /// Used as the default recorder when one has not been installed yet. Useful for acting as the root /// recorder when testing layers. +#[derive(Debug)] pub struct NoopRecorder; impl Recorder for NoopRecorder { diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 22048ac5..ee9a0f0f 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,5 @@ [toolchain] -channel = "1.70.0" +# Note that this is greater than the MSRV of the workspace (1.70) due to metrics-observer needing +# 1.74, while all the other crates only require 1.70. See +# https://github.com/metrics-rs/metrics/pull/505#discussion_r1724092556 for more information. +channel = "1.74.0"