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