glean_core/metrics/
string.rs1use std::sync::Arc;
6
7use crate::common_metric_data::{CommonMetricDataInternal, DynamicLabelType};
8use crate::error_recording::{test_get_num_recorded_errors, ErrorType};
9use crate::metrics::Metric;
10use crate::metrics::MetricType;
11use crate::storage::StorageManager;
12use crate::util::truncate_string_at_boundary_with_error;
13use crate::Glean;
14use crate::{CommonMetricData, TestGetValue};
15
16const MAX_LENGTH_VALUE: usize = 255;
17
18#[derive(Clone, Debug)]
23pub struct StringMetric {
24 meta: Arc<CommonMetricDataInternal>,
25}
26
27impl MetricType for StringMetric {
28 fn meta(&self) -> &CommonMetricDataInternal {
29 &self.meta
30 }
31
32 fn with_name(&self, name: String) -> Self {
33 let mut meta = (*self.meta).clone();
34 meta.inner.name = name;
35 Self {
36 meta: Arc::new(meta),
37 }
38 }
39
40 fn with_dynamic_label(&self, label: DynamicLabelType) -> Self {
41 let mut meta = (*self.meta).clone();
42 meta.inner.dynamic_label = Some(label);
43 Self {
44 meta: Arc::new(meta),
45 }
46 }
47}
48
49impl StringMetric {
54 pub fn new(meta: CommonMetricData) -> Self {
56 Self {
57 meta: Arc::new(meta.into()),
58 }
59 }
60
61 pub fn set(&self, value: String) {
71 let metric = self.clone();
72 crate::launch_with_glean(move |glean| metric.set_sync(glean, &value))
73 }
74
75 #[doc(hidden)]
77 pub fn set_sync<S: Into<String>>(&self, glean: &Glean, value: S) {
78 if !self.should_record(glean) {
79 return;
80 }
81
82 let s = truncate_string_at_boundary_with_error(glean, &self.meta, value, MAX_LENGTH_VALUE);
83
84 let value = Metric::String(s);
85 glean.storage().record(glean, &self.meta, &value)
86 }
87
88 #[doc(hidden)]
90 pub fn get_value<'a, S: Into<Option<&'a str>>>(
91 &self,
92 glean: &Glean,
93 ping_name: S,
94 ) -> Option<String> {
95 let queried_ping_name = ping_name
96 .into()
97 .unwrap_or_else(|| &self.meta().inner.send_in_pings[0]);
98
99 match StorageManager.snapshot_metric_for_test(
100 glean.storage(),
101 queried_ping_name,
102 &self.meta.identifier(glean),
103 self.meta.inner.lifetime,
104 ) {
105 Some(Metric::String(s)) => Some(s),
106 _ => None,
107 }
108 }
109
110 pub fn test_get_num_recorded_errors(&self, error: ErrorType) -> i32 {
122 crate::block_on_dispatcher();
123
124 crate::core::with_glean(|glean| {
125 test_get_num_recorded_errors(glean, self.meta(), error).unwrap_or(0)
126 })
127 }
128}
129
130impl TestGetValue<String> for StringMetric {
131 fn test_get_value(&self, ping_name: Option<String>) -> Option<String> {
146 crate::block_on_dispatcher();
147 crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref()))
148 }
149}
150
151#[cfg(test)]
152mod test {
153 use super::*;
154 use crate::tests::new_glean;
155 use crate::util::truncate_string_at_boundary;
156 use crate::Lifetime;
157
158 #[test]
159 fn setting_a_long_string_records_an_error() {
160 let (glean, _t) = new_glean(None);
161
162 let metric = StringMetric::new(CommonMetricData {
163 name: "string_metric".into(),
164 category: "test".into(),
165 send_in_pings: vec!["store1".into()],
166 lifetime: Lifetime::Application,
167 disabled: false,
168 dynamic_label: None,
169 });
170
171 let sample_string = "0123456789".repeat(26);
172 metric.set_sync(&glean, sample_string.clone());
173
174 let truncated = truncate_string_at_boundary(sample_string, MAX_LENGTH_VALUE);
175 assert_eq!(truncated, metric.get_value(&glean, "store1").unwrap());
176
177 assert_eq!(
178 1,
179 test_get_num_recorded_errors(&glean, metric.meta(), ErrorType::InvalidOverflow)
180 .unwrap()
181 );
182 }
183}