glean_core/metrics/
text.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 = 200 * 1024;
18
19#[derive(Clone, Debug)]
25pub struct TextMetric {
26 meta: Arc<CommonMetricDataInternal>,
27}
28
29impl MetricType for TextMetric {
30 fn meta(&self) -> &CommonMetricDataInternal {
31 &self.meta
32 }
33
34 fn with_name(&self, name: String) -> Self {
35 let mut meta = (*self.meta).clone();
36 meta.inner.name = name;
37 Self {
38 meta: Arc::new(meta),
39 }
40 }
41
42 fn with_dynamic_label(&self, label: DynamicLabelType) -> Self {
43 let mut meta = (*self.meta).clone();
44 meta.inner.dynamic_label = Some(label);
45 Self {
46 meta: Arc::new(meta),
47 }
48 }
49}
50
51impl TextMetric {
56 pub fn new(meta: CommonMetricData) -> Self {
58 Self {
59 meta: Arc::new(meta.into()),
60 }
61 }
62
63 pub fn set(&self, value: String) {
74 let metric = self.clone();
75 crate::launch_with_glean(move |glean| metric.set_sync(glean, &value))
76 }
77
78 #[doc(hidden)]
81 pub fn set_sync<S: Into<String>>(&self, glean: &Glean, value: S) {
82 if !self.should_record(glean) {
83 return;
84 }
85
86 let s = truncate_string_at_boundary_with_error(glean, &self.meta, value, MAX_LENGTH_VALUE);
87
88 let value = Metric::Text(s);
89 glean.storage().record(glean, &self.meta, &value)
90 }
91
92 #[doc(hidden)]
94 pub fn get_value<'a, S: Into<Option<&'a str>>>(
95 &self,
96 glean: &Glean,
97 ping_name: S,
98 ) -> Option<String> {
99 let queried_ping_name = ping_name
100 .into()
101 .unwrap_or_else(|| &self.meta().inner.send_in_pings[0]);
102
103 match StorageManager.snapshot_metric_for_test(
104 glean.storage(),
105 queried_ping_name,
106 &self.meta.identifier(glean),
107 self.meta.inner.lifetime,
108 ) {
109 Some(Metric::Text(s)) => Some(s),
110 _ => None,
111 }
112 }
113
114 pub fn test_get_num_recorded_errors(&self, error: ErrorType) -> i32 {
126 crate::block_on_dispatcher();
127
128 crate::core::with_glean(|glean| {
129 test_get_num_recorded_errors(glean, self.meta(), error).unwrap_or(0)
130 })
131 }
132}
133
134impl TestGetValue<String> for TextMetric {
135 fn test_get_value(&self, ping_name: Option<String>) -> Option<String> {
150 crate::block_on_dispatcher();
151 crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref()))
152 }
153}
154
155#[cfg(test)]
156mod test {
157 use super::*;
158 use crate::tests::new_glean;
159 use crate::util::truncate_string_at_boundary;
160 use crate::Lifetime;
161
162 #[test]
163 fn setting_a_long_string_records_an_error() {
164 let (glean, _t) = new_glean(None);
165
166 let metric = TextMetric::new(CommonMetricData {
167 name: "text_metric".into(),
168 category: "test".into(),
169 send_in_pings: vec!["store1".into()],
170 lifetime: Lifetime::Application,
171 disabled: false,
172 dynamic_label: None,
173 });
174
175 let sample_string = "0123456789".repeat(200 * 1024);
176 metric.set_sync(&glean, sample_string.clone());
177
178 let truncated = truncate_string_at_boundary(sample_string, MAX_LENGTH_VALUE);
179 assert_eq!(truncated, metric.get_value(&glean, "store1").unwrap());
180
181 assert_eq!(
182 1,
183 test_get_num_recorded_errors(&glean, metric.meta(), ErrorType::InvalidOverflow)
184 .unwrap()
185 );
186 }
187}