Bug 1623300 - Implement the remaining parts of the control API. r=chutten

Controlling is two things:
* initializing Glean
* toggling upload enabled.

Initializing also means setting the internal metrics (client info), some
of which we detect at build time.

`upload_enabled` will eventually be tied to the Firefox Telemetry upload preference.

Differential Revision: https://phabricator.services.mozilla.com/D68543

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Jan-Erik Rediger 2020-03-30 19:14:47 +00:00
parent d8523c3a61
commit 1eba67a2fb
6 changed files with 241 additions and 1 deletions

1
Cargo.lock generated
View File

@ -1368,6 +1368,7 @@ dependencies = [
"log",
"nserror",
"nsstring",
"once_cell",
"static_prefs",
"xpcom",
]

View File

@ -12,3 +12,4 @@ nserror = { path = "../../../xpcom/rust/nserror" }
nsstring = { path = "../../../xpcom/rust/nsstring" }
static_prefs = { path = "../../../modules/libpref/init/static_prefs" }
xpcom = { path = "../../../xpcom/rust/xpcom" }
once_cell = "1.2.0"

View File

@ -0,0 +1,125 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
//! The general API to initialize and control Glean.
//!
//! ## Example
//!
//! ```ignore
//! let configuration = Configuration { /* ... */ };
//! let client_info = ClientInfo { /* ... */ };
//!
//! // Initialize Glean once and passing in client information.
//! initialize(configuration, client_info)?;
//!
//! // Toggle the upload enabled preference.
//! set_upload_enabled(false);
//! ```
use glean_core::{global_glean, setup_glean, Configuration, Glean, Result};
use once_cell::sync::OnceCell;
use crate::client_info::ClientInfo;
use crate::core_metrics::InternalMetrics;
/// Application state to keep track of.
#[derive(Debug, Clone)]
pub struct AppState {
/// Client info metrics set by the application.
client_info: ClientInfo,
}
/// A global singleton storing additional state for Glean.
static STATE: OnceCell<AppState> = OnceCell::new();
/// Get a reference to the global state object.
///
/// Panics if no global state object was set.
fn global_state() -> &'static AppState {
STATE.get().unwrap()
}
/// Run a closure with a mutable reference to the locked global Glean object.
fn with_glean_mut<F, R>(f: F) -> R
where
F: Fn(&mut Glean) -> R,
{
let mut glean = global_glean().lock().unwrap();
f(&mut glean)
}
/// Create and initialize a new Glean object.
///
/// See `glean_core::Glean::new`.
///
/// ## Thread safety
///
/// Many threads may call `initialize` concurrently with different configuration and client info,
/// but it is guaranteed that only one function will be executed.
///
/// Glean will only be initialized exactly once with the configuration and client info obtained
/// from the first call.
/// Subsequent calls have no effect.
pub fn initialize(cfg: Configuration, client_info: ClientInfo) -> Result<()> {
STATE
.get_or_try_init(|| {
let glean = Glean::new(cfg)?;
// First initialize core metrics
initialize_core_metrics(&glean, &client_info);
// Now make this the global object available to others.
setup_glean(glean)?;
Ok(AppState { client_info })
})
.map(|_| ())
}
/// Set the application's core metrics based on the passed client info.
fn initialize_core_metrics(glean: &Glean, client_info: &ClientInfo) {
let core_metrics = InternalMetrics::new();
core_metrics
.app_build
.set(glean, &client_info.app_build[..]);
core_metrics
.app_display_version
.set(glean, &client_info.app_display_version[..]);
if let Some(app_channel) = &client_info.channel {
core_metrics.app_channel.set(glean, app_channel);
}
// FIXME(bug 1625916): OS should be handled inside glean-core.
core_metrics.os.set(glean, "unknown".to_string());
core_metrics
.os_version
.set(glean, &client_info.os_version[..]);
// FIXME(bug 1624823): Architecture should be determined at runtime
core_metrics.architecture.set(glean, "unknown");
// FIXME(bug 1625207): Device manufacturer should be made optional.
core_metrics
.device_manufacturer
.set(glean, "unknown".to_string());
// FIXME(bug 1624823): Device model should be made optional.
core_metrics.device_model.set(glean, "unknown".to_string());
}
/// Set whether upload is enabled or not.
///
/// See `glean_core::Glean.set_upload_enabled`.
pub fn set_upload_enabled(enabled: bool) -> bool {
with_glean_mut(|glean| {
let state = global_state();
let old_enabled = glean.is_upload_enabled();
glean.set_upload_enabled(enabled);
if !old_enabled && enabled {
// If uploading is being re-enabled, we have to restore the
// application-lifetime metrics.
initialize_core_metrics(&glean, &state.client_info);
}
enabled
})
}

View File

@ -3,7 +3,7 @@
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
/// Metrics included in every ping as `client_info`.
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct ClientInfo {
/// The build identifier generated by the CI system (e.g. "1234/A").
pub app_build: String,

View File

@ -0,0 +1,92 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
//! Internal metrics used to fill in the [client info section][client_info] included in every ping.
//!
//! [client_info]: https://mozilla.github.io/glean/book/user/pings/index.html#the-client_info-section
use glean_core::{metrics::StringMetric, CommonMetricData, Lifetime};
#[derive(Debug)]
pub struct InternalMetrics {
pub app_build: StringMetric,
pub app_display_version: StringMetric,
pub app_channel: StringMetric,
pub os: StringMetric,
pub os_version: StringMetric,
pub architecture: StringMetric,
pub device_manufacturer: StringMetric,
pub device_model: StringMetric,
}
impl InternalMetrics {
pub fn new() -> Self {
Self {
app_build: StringMetric::new(CommonMetricData {
name: "app_build".into(),
category: "".into(),
send_in_pings: vec!["glean_client_info".into()],
lifetime: Lifetime::Application,
disabled: false,
dynamic_label: None,
}),
app_display_version: StringMetric::new(CommonMetricData {
name: "app_display_version".into(),
category: "".into(),
send_in_pings: vec!["glean_client_info".into()],
lifetime: Lifetime::Application,
disabled: false,
dynamic_label: None,
}),
app_channel: StringMetric::new(CommonMetricData {
name: "app_channel".into(),
category: "".into(),
send_in_pings: vec!["glean_client_info".into()],
lifetime: Lifetime::Application,
disabled: false,
dynamic_label: None,
}),
os: StringMetric::new(CommonMetricData {
name: "os".into(),
category: "".into(),
send_in_pings: vec!["glean_client_info".into()],
lifetime: Lifetime::Application,
disabled: false,
dynamic_label: None,
}),
os_version: StringMetric::new(CommonMetricData {
name: "os_version".into(),
category: "".into(),
send_in_pings: vec!["glean_client_info".into()],
lifetime: Lifetime::Application,
disabled: false,
dynamic_label: None,
}),
architecture: StringMetric::new(CommonMetricData {
name: "architecture".into(),
category: "".into(),
send_in_pings: vec!["glean_client_info".into()],
lifetime: Lifetime::Application,
disabled: false,
dynamic_label: None,
}),
device_manufacturer: StringMetric::new(CommonMetricData {
name: "device_manufacturer".into(),
category: "".into(),
send_in_pings: vec!["glean_client_info".into()],
lifetime: Lifetime::Application,
disabled: false,
dynamic_label: None,
}),
device_model: StringMetric::new(CommonMetricData {
name: "device_model".into(),
category: "".into(),
send_in_pings: vec!["glean_client_info".into()],
lifetime: Lifetime::Application,
disabled: false,
dynamic_label: None,
}),
}
}
}

View File

@ -2,6 +2,21 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
//! Firefox on Glean (FOG) is the name of the layer that integrates the [Glean SDK][glean-sdk] into Firefox Desktop.
//! It is currently being designed and implemented.
//!
//! The [Glean SDK][glean-sdk] is a data collection library built by Mozilla for use in its products.
//! Like [Telemetry][telemetry], it can be used to
//! (in accordance with our [Privacy Policy][privacy-policy])
//! send anonymous usage statistics to Mozilla in order to make better decisions.
//!
//! Documentation can be found online in the [Firefox Source Docs][docs].
//!
//! [glean-sdk]: https://github.com/mozilla/glean/
//! [book-of-glean]: https://mozilla.github.io/glean/book/index.html
//! [privacy-policy]: https://www.mozilla.org/privacy/
//! [docs]: https://firefox-source-docs.mozilla.org/toolkit/components/glean/
use std::ffi::CStr;
use std::os::raw::c_char;
@ -10,8 +25,14 @@ use nserror::{nsresult, NS_OK};
use client_info::ClientInfo;
use glean_core::Configuration;
mod api;
mod client_info;
mod core_metrics;
/// Project FOG's entry point.
///
/// This assembles client information and the Glean configuration and then initializes the global
/// Glean instance.
#[no_mangle]
pub unsafe extern "C" fn fog_init(
app_build: *const c_char,