in glean-core/src/lib.rs [376:578]
fn initialize_inner(
cfg: InternalConfiguration,
client_info: ClientInfoMetrics,
callbacks: Box<dyn OnGleanEvents>,
) {
if was_initialize_called() {
log::error!("Glean should not be initialized multiple times");
return;
}
let init_handle = std::thread::Builder::new()
.name("glean.init".into())
.spawn(move || {
let upload_enabled = cfg.upload_enabled;
let trim_data_to_registered_pings = cfg.trim_data_to_registered_pings;
// Set the internal logging level.
if let Some(level) = cfg.log_level {
log::set_max_level(level)
}
let glean = match Glean::new(cfg) {
Ok(glean) => glean,
Err(err) => {
log::error!("Failed to initialize Glean: {}", err);
return;
}
};
if core::setup_glean(glean).is_err() {
return;
}
log::info!("Glean initialized");
setup_state(State {
client_info,
callbacks,
});
let mut is_first_run = false;
let mut dirty_flag = false;
let mut pings_submitted = false;
core::with_glean_mut(|glean| {
// The debug view tag might have been set before initialize,
// get the cached value and set it.
let debug_tag = PRE_INIT_DEBUG_VIEW_TAG.lock().unwrap();
if !debug_tag.is_empty() {
glean.set_debug_view_tag(&debug_tag);
}
// The log pings debug option might have been set before initialize,
// get the cached value and set it.
let log_pigs = PRE_INIT_LOG_PINGS.load(Ordering::SeqCst);
if log_pigs {
glean.set_log_pings(log_pigs);
}
// The source tags might have been set before initialize,
// get the cached value and set them.
let source_tags = PRE_INIT_SOURCE_TAGS.lock().unwrap();
if !source_tags.is_empty() {
glean.set_source_tags(source_tags.to_vec());
}
// Get the current value of the dirty flag so we know whether to
// send a dirty startup baseline ping below. Immediately set it to
// `false` so that dirty startup pings won't be sent if Glean
// initialization does not complete successfully.
dirty_flag = glean.is_dirty_flag_set();
glean.set_dirty_flag(false);
// Perform registration of pings that were attempted to be
// registered before init.
let pings = PRE_INIT_PING_REGISTRATION.lock().unwrap();
for ping in pings.iter() {
glean.register_ping_type(ping);
}
let pings = PRE_INIT_PING_ENABLED.lock().unwrap();
for (ping, enabled) in pings.iter() {
glean.set_ping_enabled(ping, *enabled);
}
// The attribution and distribution might have been set before initialize,
// take the cached values and set them.
if let Some(attribution) = PRE_INIT_ATTRIBUTION.lock().unwrap().take() {
glean.update_attribution(attribution);
}
if let Some(distribution) = PRE_INIT_DISTRIBUTION.lock().unwrap().take() {
glean.update_distribution(distribution);
}
// If this is the first time ever the Glean SDK runs, make sure to set
// some initial core metrics in case we need to generate early pings.
// The next times we start, we would have them around already.
is_first_run = glean.is_first_run();
if is_first_run {
let state = global_state().lock().unwrap();
initialize_core_metrics(glean, &state.client_info);
}
// Deal with any pending events so we can start recording new ones
pings_submitted = glean.on_ready_to_submit_pings(trim_data_to_registered_pings);
});
{
let state = global_state().lock().unwrap();
// We need to kick off upload in these cases:
// 1. Pings were submitted through Glean and it is ready to upload those pings;
// 2. Upload is disabled, to upload a possible deletion-request ping.
if pings_submitted || !upload_enabled {
if let Err(e) = state.callbacks.trigger_upload() {
log::error!("Triggering upload failed. Error: {}", e);
}
}
}
core::with_glean(|glean| {
// Start the MPS if its handled within Rust.
glean.start_metrics_ping_scheduler();
});
// The metrics ping scheduler might _synchronously_ submit a ping
// so that it runs before we clear application-lifetime metrics further below.
// For that it needs access to the `Glean` object.
// Thus we need to unlock that by leaving the context above,
// then re-lock it afterwards.
// That's safe because user-visible functions will be queued and thus not execute until
// we unblock later anyway.
{
let state = global_state().lock().unwrap();
// Set up information and scheduling for Glean owned pings. Ideally, the "metrics"
// ping startup check should be performed before any other ping, since it relies
// on being dispatched to the API context before any other metric.
if state.callbacks.start_metrics_ping_scheduler() {
if let Err(e) = state.callbacks.trigger_upload() {
log::error!("Triggering upload failed. Error: {}", e);
}
}
}
core::with_glean_mut(|glean| {
let state = global_state().lock().unwrap();
// Check if the "dirty flag" is set. That means the product was probably
// force-closed. If that's the case, submit a 'baseline' ping with the
// reason "dirty_startup". We only do that from the second run.
if !is_first_run && dirty_flag {
// The `submit_ping_by_name_sync` function cannot be used, otherwise
// startup will cause a dead-lock, since that function requests a
// write lock on the `glean` object.
// Note that unwrapping below is safe: the function will return an
// `Ok` value for a known ping.
if glean.submit_ping_by_name("baseline", Some("dirty_startup")) {
if let Err(e) = state.callbacks.trigger_upload() {
log::error!("Triggering upload failed. Error: {}", e);
}
}
}
// From the second time we run, after all startup pings are generated,
// make sure to clear `lifetime: application` metrics and set them again.
// Any new value will be sent in newly generated pings after startup.
if !is_first_run {
glean.clear_application_lifetime_metrics();
initialize_core_metrics(glean, &state.client_info);
}
});
// Signal Dispatcher that init is complete
// bug 1839433: It is important that this happens after any init tasks
// that shutdown() depends on. At time of writing that's only setting up
// the global Glean, but it is probably best to flush the preinit queue
// as late as possible in the glean.init thread.
match dispatcher::flush_init() {
Ok(task_count) if task_count > 0 => {
core::with_glean(|glean| {
glean_metrics::error::preinit_tasks_overflow
.add_sync(glean, task_count as i32);
});
}
Ok(_) => {}
Err(err) => log::error!("Unable to flush the preinit queue: {}", err),
}
let state = global_state().lock().unwrap();
state.callbacks.initialize_finished();
})
.expect("Failed to spawn Glean's init thread");
// For test purposes, store the glean init thread's JoinHandle.
INIT_HANDLES.lock().unwrap().push(init_handle);
// Mark the initialization as called: this needs to happen outside of the
// dispatched block!
INITIALIZE_CALLED.store(true, Ordering::SeqCst);
// In test mode we wait for initialization to finish.
// This needs to run after we set `INITIALIZE_CALLED`, so it's similar to normal behavior.
if dispatcher::global::is_test_mode() {
join_init();
}
}