Skip to main content

glean_preview/
lib.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5#![deny(missing_docs)]
6
7//! Glean is a modern approach for recording and sending Telemetry data.
8//!
9//! It's in use at Mozilla.
10//!
11//! All documentation can be found online:
12//!
13//! ## [The Glean SDK Book](https://mozilla.github.io/glean)
14//!
15//! ## Example
16//!
17//! Initialize Glean, register a ping and then send it.
18//!
19//! ```rust,no_run
20//! # use glean_preview::{Configuration, ClientInfoMetrics, Error, metrics::*};
21//! # fn main() -> Result<(), Error> {
22//! let cfg = Configuration {
23//!     data_path: "/tmp/data".into(),
24//!     application_id: "org.mozilla.glean_core.example".into(),
25//!     upload_enabled: true,
26//!     max_events: None,
27//!     delay_ping_lifetime_io: false,
28//!     channel: None,
29//! };
30//! glean_preview::initialize(cfg, ClientInfoMetrics::unknown())?;
31//!
32//! let prototype_ping = PingType::new("prototype", true, true);
33//!
34//! glean_preview::register_ping_type(&prototype_ping);
35//!
36//! prototype_ping.submit();
37//! # Ok(())
38//! # }
39//! ```
40
41use once_cell::sync::OnceCell;
42use std::sync::Mutex;
43
44pub use configuration::Configuration;
45pub use core_metrics::ClientInfoMetrics;
46pub use glean_core::{CommonMetricData, Error, Glean, Lifetime, Result};
47
48mod configuration;
49mod core_metrics;
50pub mod metrics;
51mod system;
52
53#[derive(Debug)]
54struct GleanWrapper {
55    instance: Glean,
56    channel: Option<String>,
57    client_info: ClientInfoMetrics,
58}
59
60static GLEAN: OnceCell<Mutex<GleanWrapper>> = OnceCell::new();
61
62/// Get a reference to the global Glean object.
63///
64/// Panics if no global Glean object was set.
65fn global_glean() -> &'static Mutex<GleanWrapper> {
66    GLEAN.get().unwrap()
67}
68
69/// Set or replace the global Glean object.
70fn setup_glean(glean: GleanWrapper) -> Result<()> {
71    if GLEAN.get().is_none() {
72        GLEAN.set(Mutex::new(glean)).unwrap();
73    } else {
74        let mut lock = GLEAN.get().unwrap().lock().unwrap();
75        *lock = glean;
76    }
77    Ok(())
78}
79
80fn with_glean<F, R>(f: F) -> R
81where
82    F: Fn(&Glean) -> R,
83{
84    let lock = global_glean().lock().unwrap();
85    f(&lock.instance)
86}
87
88fn with_glean_wrapper_mut<F, R>(f: F) -> R
89where
90    F: Fn(&mut GleanWrapper) -> R,
91{
92    let mut lock = global_glean().lock().unwrap();
93    f(&mut lock)
94}
95
96fn with_glean_mut<F, R>(f: F) -> R
97where
98    F: Fn(&mut Glean) -> R,
99{
100    let mut lock = global_glean().lock().unwrap();
101    f(&mut lock.instance)
102}
103
104/// Create and initialize a new Glean object.
105///
106/// See `glean_core::Glean::new`.
107pub fn initialize(cfg: Configuration, client_info: ClientInfoMetrics) -> Result<()> {
108    let core_cfg = glean_core::Configuration {
109        upload_enabled: cfg.upload_enabled,
110        data_path: cfg.data_path.clone(),
111        application_id: cfg.application_id.clone(),
112        max_events: cfg.max_events,
113        delay_ping_lifetime_io: cfg.delay_ping_lifetime_io,
114    };
115    let glean = Glean::new(core_cfg)?;
116
117    // First initialize core metrics
118    initialize_core_metrics(&glean, &client_info, cfg.channel.clone());
119
120    // Now make this the global object available to others.
121    let wrapper = GleanWrapper {
122        instance: glean,
123        channel: cfg.channel,
124        client_info,
125    };
126    setup_glean(wrapper)?;
127
128    Ok(())
129}
130
131fn initialize_core_metrics(
132    glean: &Glean,
133    client_info: &ClientInfoMetrics,
134    channel: Option<String>,
135) {
136    let core_metrics = core_metrics::InternalMetrics::new();
137
138    core_metrics
139        .app_build
140        .set(glean, &client_info.app_build[..]);
141    core_metrics
142        .app_display_version
143        .set(glean, &client_info.app_display_version[..]);
144    if let Some(app_channel) = channel {
145        core_metrics.app_channel.set(glean, app_channel);
146    }
147    core_metrics.os.set(glean, system::OS.to_string());
148    core_metrics.os_version.set(glean, "unknown".to_string());
149    core_metrics
150        .architecture
151        .set(glean, system::ARCH.to_string());
152    core_metrics
153        .device_manufacturer
154        .set(glean, "unknown".to_string());
155    core_metrics.device_model.set(glean, "unknown".to_string());
156}
157
158/// Set whether upload is enabled or not.
159///
160/// See `glean_core::Glean.set_upload_enabled`.
161pub fn set_upload_enabled(enabled: bool) -> bool {
162    with_glean_wrapper_mut(|glean| {
163        let old_enabled = glean.instance.is_upload_enabled();
164        glean.instance.set_upload_enabled(enabled);
165
166        if !old_enabled && enabled {
167            // If uploading is being re-enabled, we have to restore the
168            // application-lifetime metrics.
169            initialize_core_metrics(&glean.instance, &glean.client_info, glean.channel.clone());
170        }
171
172        enabled
173    })
174}
175
176/// Determine whether upload is enabled.
177///
178/// See `glean_core::Glean.is_upload_enabled`.
179pub fn is_upload_enabled() -> bool {
180    with_glean(|glean| glean.is_upload_enabled())
181}
182
183/// Register a new [`PingType`](metrics/struct.PingType.html).
184pub fn register_ping_type(ping: &metrics::PingType) {
185    with_glean_mut(|glean| {
186        glean.register_ping_type(&ping.ping_type);
187    })
188}
189
190/// Collect and submit a ping for eventual uploading.
191///
192/// See `glean_core::Glean.submit_ping`.
193///
194/// ## Return value
195///
196/// Returns true if a ping was assembled and queued, false otherwise.
197pub fn submit_ping(ping: &metrics::PingType) -> bool {
198    submit_ping_by_name(&ping.name)
199}
200
201/// Collect and submit a ping for eventual uploading by name.
202///
203/// See `glean_core::Glean.submit_ping_by_name`.
204///
205/// ## Return value
206///
207/// Returns true if a ping was assembled and queued, false otherwise.
208pub fn submit_ping_by_name(ping: &str) -> bool {
209    submit_pings_by_name(&[ping.to_string()])
210}
211
212/// Collect and submit multiple pings by name for eventual uploading.
213///
214/// ## Return value
215///
216/// Returns true if at least one ping was assembled and queued, false otherwise.
217pub fn submit_pings_by_name(pings: &[String]) -> bool {
218    with_glean(|glean| glean.submit_pings_by_name(pings))
219}
220
221#[cfg(test)]
222mod test;