remote_settings/
telemetry.rs1use std::{fmt, sync::Arc};
6
7use crate::error::Error;
8
9#[derive(Debug, PartialEq, uniffi::Enum)]
11pub enum SyncStatus {
12 Success,
14 UpToDate,
16 NetworkError,
18 BackoffError,
20 SignatureError,
22 ServerError,
24 UnknownError,
26}
27
28impl fmt::Display for SyncStatus {
29 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30 let s = match self {
31 SyncStatus::Success => "success",
32 SyncStatus::UpToDate => "up_to_date",
33 SyncStatus::NetworkError => "network_error",
34 SyncStatus::BackoffError => "backoff_error",
35 SyncStatus::SignatureError => "signature_error",
36 SyncStatus::ServerError => "server_error",
37 SyncStatus::UnknownError => "unknown_error",
38 };
39 f.write_str(s)
40 }
41}
42
43#[derive(Debug, PartialEq, uniffi::Record, Default)]
44pub struct UptakeEventExtras {
45 pub value: Option<String>,
47 pub source: Option<String>,
49 pub age: Option<String>,
51 pub trigger: Option<String>,
53 pub timestamp: Option<String>,
55 pub duration: Option<String>,
57 pub error_name: Option<String>,
59}
60
61#[uniffi::export(with_foreign)]
83pub trait RemoteSettingsTelemetry: Send + Sync {
84 fn report_uptake(&self, extras: UptakeEventExtras);
86}
87
88struct NoopRemoteSettingsTelemetry;
89
90impl RemoteSettingsTelemetry for NoopRemoteSettingsTelemetry {
91 fn report_uptake(&self, _extras: UptakeEventExtras) {}
92}
93
94#[derive(Clone)]
96pub struct RemoteSettingsTelemetryWrapper {
97 inner: Arc<dyn RemoteSettingsTelemetry>,
98}
99
100impl RemoteSettingsTelemetryWrapper {
101 pub fn new(inner: Arc<dyn RemoteSettingsTelemetry>) -> Self {
102 Self { inner }
103 }
104
105 pub fn noop() -> Self {
106 Self {
107 inner: Arc::new(NoopRemoteSettingsTelemetry),
108 }
109 }
110
111 pub fn report_uptake_success(&self, source: &str, duration: Option<u64>) {
112 self.inner.report_uptake(UptakeEventExtras {
113 value: Some(SyncStatus::Success.to_string()),
114 source: Some(source.to_string()),
115 age: None,
116 trigger: None,
117 timestamp: None,
118 duration: duration.map(|d| d.to_string()),
119 error_name: None,
120 });
121 }
122
123 pub fn report_uptake_up_to_date(&self, source: &str, duration: Option<u64>) {
124 self.inner.report_uptake(UptakeEventExtras {
125 value: Some(SyncStatus::UpToDate.to_string()),
126 source: Some(source.to_string()),
127 age: None,
128 trigger: None,
129 timestamp: None,
130 duration: duration.map(|d| d.to_string()),
131 error_name: None,
132 });
133 }
134
135 pub fn report_uptake_error(&self, error: &Error, source: &str) {
136 let error_name = format!("{error:?}")
141 .split(&['{', '('])
142 .next()
143 .unwrap_or("")
144 .trim()
145 .to_string();
146 self.inner.report_uptake(UptakeEventExtras {
147 value: Some(error_to_status(error).to_string()),
148 source: Some(source.to_string()),
149 age: None,
150 trigger: None,
151 timestamp: None,
152 duration: None,
153 error_name: Some(error_name),
154 });
155 }
156}
157
158fn error_to_status(error: &Error) -> SyncStatus {
159 match error {
160 Error::RequestError(viaduct::ViaductError::NetworkError(_))
161 | Error::ResponseError { .. } => SyncStatus::NetworkError,
162 Error::BackoffError(_) => SyncStatus::BackoffError,
163 #[cfg(feature = "signatures")]
164 Error::IncompleteSignatureDataError(_) => SyncStatus::SignatureError,
165 #[cfg(feature = "signatures")]
166 Error::SignatureError(_) => SyncStatus::SignatureError,
167 _ => SyncStatus::UnknownError,
168 }
169}