diff --git a/sentry-core/src/metrics/mod.rs b/sentry-core/src/metrics/mod.rs index f4a38726..65cd7478 100644 --- a/sentry-core/src/metrics/mod.rs +++ b/sentry-core/src/metrics/mod.rs @@ -54,9 +54,6 @@ use std::sync::{Arc, Mutex}; use std::thread::{self, JoinHandle}; use std::time::{Duration, SystemTime, UNIX_EPOCH}; -use normalization::normalized_name::NormalizedName; -use normalization::normalized_tags::NormalizedTags; -use normalization::normalized_unit::NormalizedUnit; use sentry_types::protocol::latest::{Envelope, EnvelopeItem}; use crate::client::TransportArc; @@ -534,11 +531,11 @@ impl Metric { .as_secs(); let data = format!( "{}@{}:{}|{}|#{}|T{}", - NormalizedName::from(self.name.as_ref()), - NormalizedUnit::from(self.unit.to_string().as_ref()), + normalization::normalize_name(self.name.as_ref()), + normalization::normalize_unit(self.unit.to_string().as_ref()), self.value, self.value.ty(), - NormalizedTags::from(&self.tags), + normalization::normalize_tags(&self.tags), timestamp ); EnvelopeItem::Statsd(data.into_bytes()).into() @@ -836,10 +833,14 @@ impl Worker { for (timestamp, buckets) in buckets { for (key, value) in buckets { - write!(&mut out, "{}", NormalizedName::from(key.name.as_ref()))?; + write!( + &mut out, + "{}", + normalization::normalize_name(key.name.as_ref()) + )?; match key.unit { MetricUnit::Custom(u) => { - write!(&mut out, "@{}", NormalizedUnit::from(u.as_ref()))? + write!(&mut out, "@{}", normalization::normalize_unit(u.as_ref()))? } _ => write!(&mut out, "@{}", key.unit)?, } @@ -868,7 +869,7 @@ impl Worker { write!(&mut out, "|{}", key.ty.as_str())?; let normalized_tags = - NormalizedTags::from(&key.tags).with_default_tags(&self.default_tags); + normalization::normalize_tags(&key.tags).with_default_tags(&self.default_tags); write!(&mut out, "|#{}", normalized_tags)?; writeln!(&mut out, "|T{}", timestamp)?; } diff --git a/sentry-core/src/metrics/normalization/mod.rs b/sentry-core/src/metrics/normalization/mod.rs index e316dd98..2b228b90 100644 --- a/sentry-core/src/metrics/normalization/mod.rs +++ b/sentry-core/src/metrics/normalization/mod.rs @@ -2,6 +2,10 @@ pub mod normalized_name; pub mod normalized_tags; pub mod normalized_unit; +pub use normalized_name::normalize_name; +pub use normalized_tags::normalize_tags; +pub use normalized_unit::normalize_unit; + pub fn truncate(s: &str, max_chars: usize) -> &str { match s.char_indices().nth(max_chars) { None => s, diff --git a/sentry-core/src/metrics/normalization/normalized_name.rs b/sentry-core/src/metrics/normalization/normalized_name.rs index 00277fdc..4abc2fe1 100644 --- a/sentry-core/src/metrics/normalization/normalized_name.rs +++ b/sentry-core/src/metrics/normalization/normalized_name.rs @@ -2,36 +2,21 @@ use std::{borrow::Cow, sync::OnceLock}; use regex::Regex; -pub struct NormalizedName<'a> { - name: Cow<'a, str>, -} - -impl<'a> From<&'a str> for NormalizedName<'a> { - fn from(name: &'a str) -> Self { - static METRIC_NAME_RE: OnceLock = OnceLock::new(); - Self { - name: METRIC_NAME_RE - .get_or_init(|| Regex::new(r"[^a-zA-Z0-9_\-.]").expect("Regex should compile")) - .replace_all(super::truncate(name, 150), "_"), - } - } -} - -impl std::fmt::Display for NormalizedName<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "{}", self.name) - } +pub fn normalize_name(name: &str) -> Cow { + static METRIC_NAME_RE: OnceLock = OnceLock::new(); + METRIC_NAME_RE + .get_or_init(|| Regex::new(r"[^a-zA-Z0-9_\-.]").expect("Regex should compile")) + .replace_all(super::truncate(name, 150), "_") } #[cfg(test)] mod test { - use crate::metrics::NormalizedName; #[test] fn test_from() { let expected = "aA1_-.____________"; - let actual = NormalizedName::from("aA1_-./+รถ{๐Ÿ˜€\n\t\r\\| ,").to_string(); + let actual = super::normalize_name("aA1_-./+รถ{๐Ÿ˜€\n\t\r\\| ,"); assert_eq!(expected, actual); } @@ -40,7 +25,8 @@ mod test { fn test_length_restriction() { let expected = "a".repeat(150); - let actual = NormalizedName::from("a".repeat(155).as_ref()).to_string(); + let too_long_name = "a".repeat(155); + let actual = super::normalize_name(&too_long_name); assert_eq!(expected, actual); } diff --git a/sentry-core/src/metrics/normalization/normalized_tags.rs b/sentry-core/src/metrics/normalization/normalized_tags.rs index 27fe7a46..e4d713fb 100644 --- a/sentry-core/src/metrics/normalization/normalized_tags.rs +++ b/sentry-core/src/metrics/normalization/normalized_tags.rs @@ -4,12 +4,16 @@ use std::{borrow::Cow, collections::HashMap, sync::OnceLock}; use crate::metrics::TagMap; +pub fn normalize_tags(tags: &TagMap) -> NormalizedTags { + NormalizedTags::from_tag_map(tags) +} + pub struct NormalizedTags<'a> { tags: HashMap, String>, } -impl<'a> From<&'a TagMap> for NormalizedTags<'a> { - fn from(tags: &'a TagMap) -> Self { +impl<'a> NormalizedTags<'a> { + fn from_tag_map(tags: &'a TagMap) -> Self { Self { tags: tags .iter() @@ -95,7 +99,7 @@ mod test { ); let expected = "aa:a\\na,bb:b\\rb,cc:c\\tc,dd:d\\\\d,ee:e\\u{7c}e,ff:f\\u{2c}f"; - let actual = NormalizedTags::from(&tags).to_string(); + let actual = NormalizedTags::from_tag_map(&tags).to_string(); assert_eq!(expected, actual); } @@ -109,7 +113,7 @@ mod test { ); let expected = ""; - let actual = NormalizedTags::from(&tags).to_string(); + let actual = NormalizedTags::from_tag_map(&tags).to_string(); assert_eq!(expected, actual); } @@ -119,7 +123,7 @@ mod test { let tags = TagMap::from([("aA1_-./+รถ{ ๐Ÿ˜€".into(), "aA1_-./+รถ{ ๐Ÿ˜€".into())]); let expected = "aA1_-./:aA1_-./+รถ{ ๐Ÿ˜€"; - let actual = NormalizedTags::from(&tags).to_string(); + let actual = NormalizedTags::from_tag_map(&tags).to_string(); assert_eq!(expected, actual); } @@ -132,7 +136,7 @@ mod test { ]); let expected = "environment:production,release:default_release"; - let actual = NormalizedTags::from(&TagMap::new()) + let actual = NormalizedTags::from_tag_map(&TagMap::new()) .with_default_tags(&default_tags) .to_string(); @@ -147,7 +151,7 @@ mod test { ]); let expected = "environment:custom_env,release:custom_release"; - let actual = NormalizedTags::from(&TagMap::from([ + let actual = NormalizedTags::from_tag_map(&TagMap::from([ ("release".into(), "custom_release".into()), ("environment".into(), "custom_env".into()), ])) @@ -167,7 +171,7 @@ mod test { + ":" + "v".repeat(200).as_str(); - let actual = NormalizedTags::from(&TagMap::from([( + let actual = NormalizedTags::from_tag_map(&TagMap::from([( "k".repeat(35).into(), "v".repeat(210).into(), )])) diff --git a/sentry-core/src/metrics/normalization/normalized_unit.rs b/sentry-core/src/metrics/normalization/normalized_unit.rs index 40fbb04e..e6b488b1 100644 --- a/sentry-core/src/metrics/normalization/normalized_unit.rs +++ b/sentry-core/src/metrics/normalization/normalized_unit.rs @@ -4,40 +4,26 @@ use regex::Regex; use crate::units::MetricUnit; -pub struct NormalizedUnit<'a> { - unit: Cow<'a, str>, -} - -impl<'a> From<&'a str> for NormalizedUnit<'a> { - fn from(unit: &'a str) -> Self { - static METRIC_UNIT_RE: OnceLock = OnceLock::new(); - let normalized_unit = METRIC_UNIT_RE - .get_or_init(|| Regex::new(r"[^a-zA-Z0-9_]").expect("Regex should compile")) - .replace_all(super::truncate(unit, 15), ""); - Self { - unit: match normalized_unit.is_empty() { - true => MetricUnit::None.to_string().into(), - false => normalized_unit, - }, - } - } -} - -impl std::fmt::Display for NormalizedUnit<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.unit) +pub fn normalize_unit(unit: &str) -> Cow { + static METRIC_UNIT_RE: OnceLock = OnceLock::new(); + let normalized_unit = METRIC_UNIT_RE + .get_or_init(|| Regex::new(r"[^a-zA-Z0-9_]").expect("Regex should compile")) + .replace_all(super::truncate(unit, 15), ""); + if normalized_unit.is_empty() { + MetricUnit::None.to_string().into() + } else { + normalized_unit } } #[cfg(test)] mod test { - use crate::metrics::NormalizedUnit; #[test] fn test_from() { let expected = "aA1_"; - let actual = NormalizedUnit::from("aA1_-./+รถ{๐Ÿ˜€\n\t\r\\| ,").to_string(); + let actual = super::normalize_unit("aA1_-./+รถ{๐Ÿ˜€\n\t\r\\| ,").to_string(); assert_eq!(expected, actual); } @@ -46,7 +32,7 @@ mod test { fn test_from_empty() { let expected = "none"; - let actual = NormalizedUnit::from("").to_string(); + let actual = super::normalize_unit("").to_string(); assert_eq!(expected, actual); } @@ -55,7 +41,7 @@ mod test { fn test_from_empty_after_normalization() { let expected = "none"; - let actual = NormalizedUnit::from("+").to_string(); + let actual = super::normalize_unit("+").to_string(); assert_eq!(expected, actual); } @@ -64,7 +50,7 @@ mod test { fn test_length_restriction() { let expected = "a".repeat(15); - let actual = NormalizedUnit::from("a".repeat(20).as_ref()).to_string(); + let actual = super::normalize_unit("a".repeat(20).as_ref()).to_string(); assert_eq!(expected, actual); }