glean_core/metrics/
string.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
15pub const MAX_LENGTH_VALUE: usize = 255;
16
17#[derive(Clone, Debug)]
22pub struct StringMetric {
23 meta: Arc<CommonMetricDataInternal>,
24}
25
26impl MetricType for StringMetric {
27 fn meta(&self) -> &CommonMetricDataInternal {
28 &self.meta
29 }
30
31 fn with_name(&self, name: String) -> Self {
32 let mut meta = (*self.meta).clone();
33 meta.inner.name = name;
34 Self {
35 meta: Arc::new(meta),
36 }
37 }
38
39 fn with_label(&self, label: MetricLabel) -> Self {
40 let mut meta = (*self.meta).clone();
41 meta.inner.label = Some(label);
42 Self {
43 meta: Arc::new(meta),
44 }
45 }
46}
47
48impl StringMetric {
53 pub fn new(meta: CommonMetricData) -> Self {
55 Self {
56 meta: Arc::new(meta.into()),
57 }
58 }
59
60 pub fn set(&self, value: String) {
70 let metric = self.clone();
71 crate::launch_with_glean(move |glean| metric.set_sync(glean, &value))
72 }
73
74 #[doc(hidden)]
76 pub fn set_sync<S: Into<String>>(&self, glean: &Glean, value: S) {
77 self.set_sync_with_max_length(glean, value, MAX_LENGTH_VALUE)
78 }
79
80 pub(crate) fn set_sync_with_max_length<S: Into<String>>(
82 &self,
83 glean: &Glean,
84 value: S,
85 max_length: usize,
86 ) {
87 if !self.should_record(glean) {
88 return;
89 }
90
91 let s = truncate_string_at_boundary_with_error(glean, &self.meta, value, max_length);
92
93 let value = Metric::String(s);
94 glean.storage().record(glean, &self.meta, &value)
95 }
96
97 #[doc(hidden)]
99 pub fn get_value<'a, S: Into<Option<&'a str>>>(
100 &self,
101 glean: &Glean,
102 ping_name: S,
103 ) -> Option<String> {
104 let queried_ping_name = ping_name
105 .into()
106 .unwrap_or_else(|| &self.meta().inner.send_in_pings[0]);
107
108 match glean.storage().get_metric(
109 #[cfg(not(feature = "sqlite"))]
110 glean,
111 self.meta(),
112 queried_ping_name,
113 ) {
114 Some(Metric::String(s)) => Some(s),
115 _ => None,
116 }
117 }
118
119 pub fn test_get_num_recorded_errors(&self, error: ErrorType) -> i32 {
131 crate::block_on_dispatcher();
132
133 crate::core::with_glean(|glean| self.test_get_num_recorded_errors_sync(glean, error))
134 }
135
136 pub(crate) fn test_get_num_recorded_errors_sync(&self, glean: &Glean, error: ErrorType) -> i32 {
138 test_get_num_recorded_errors(glean, self.meta(), error).unwrap_or(0)
139 }
140}
141
142impl TestGetValue for StringMetric {
143 type Output = String;
144
145 fn test_get_value(&self, ping_name: Option<String>) -> Option<String> {
160 crate::block_on_dispatcher();
161 crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref()))
162 }
163}
164
165#[cfg(test)]
166mod test {
167 use super::*;
168 use crate::tests::new_glean;
169 use crate::util::truncate_string_at_boundary;
170 use crate::Lifetime;
171
172 #[test]
173 fn setting_a_long_string_records_an_error() {
174 let (glean, _t) = new_glean(None);
175
176 let metric = StringMetric::new(CommonMetricData {
177 name: "string_metric".into(),
178 category: "test".into(),
179 send_in_pings: vec!["store1".into()],
180 lifetime: Lifetime::Application,
181 disabled: false,
182 label: None,
183 in_session: false,
184 });
185
186 let sample_string = "0123456789".repeat(26);
187 metric.set_sync(&glean, sample_string.clone());
188
189 let truncated = truncate_string_at_boundary(sample_string, MAX_LENGTH_VALUE);
190 assert_eq!(truncated, metric.get_value(&glean, "store1").unwrap());
191
192 assert_eq!(
193 1,
194 test_get_num_recorded_errors(&glean, metric.meta(), ErrorType::InvalidOverflow)
195 .unwrap()
196 );
197 }
198}