/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use firefox_on_glean::factory; use firefox_on_glean::private::traits::HistogramType; use firefox_on_glean::private::{CommonMetricData, Lifetime, MemoryUnit, TimeUnit}; use nserror::{nsresult, NS_ERROR_FAILURE, NS_OK}; #[cfg(feature = "with_gecko")] use nsstring::{nsACString, nsAString, nsCString}; use serde::Deserialize; use std::borrow::Cow; use std::collections::BTreeMap; use std::fs::File; use std::io::BufReader; use thin_vec::ThinVec;
/// Test-only method. /// /// Registers a metric. /// Doesn't check to see if it's been registered before. /// Doesn't check that it would pass schema validation if it were a real metric. /// /// `extra_args` is a JSON-encoded string in a form that serde can read into an ExtraMetricArgs. /// /// No effort has been made to make this pleasant to use, since it's for /// internal testing only (ie, the testing of JOG itself). #[cfg(feature = "with_gecko")] #[no_mangle] pubextern"C"fn jog_test_register_metric(
metric_type: &nsACString,
category: &nsACString,
name: &nsACString,
send_in_pings: &ThinVec<nsCString>,
lifetime: &nsACString,
disabled: bool,
extra_args: &nsACString,
) -> u32 {
log::warn!("Type: {:?}, Category: {:?}, Name: {:?}, SendInPings: {:?}, Lifetime: {:?}, Disabled: {}, ExtraArgs: {}",
metric_type, category, name, send_in_pings, lifetime, disabled, extra_args); let metric_type = &metric_type.to_utf8(); let category = category.to_string(); let name = name.to_string(); let send_in_pings = send_in_pings.iter().map(|ping| ping.to_string()).collect(); let lifetime = serde_json::from_str(&lifetime.to_utf8())
.expect("Lifetime didn't deserialize happily. Is it valid JSON?");
let extra_args: ExtraMetricArgs = if extra_args.is_empty() {
Default::default()
} else {
serde_json::from_str(&extra_args.to_utf8())
.expect("Extras didn't deserialize happily. Are they valid JSON?")
};
create_and_register_metric(
metric_type,
category,
name,
send_in_pings,
lifetime,
disabled,
extra_args,
)
.expect("Creation/Registration of metric failed") // ok to panic in test-only method
.0
}
/// Creates and registers a metric as specified, /// making it and its APIs available on the JS Glean global. /// /// Not necessary for most uses of FOG and Glean. /// If you're not sure if you should call this, /// err on the side of not calling it. /// /// # Arguments /// /// * `metric_type` - The type of metric (e.g., "counter", "string", etc.) /// * `category` - The category/namespace for the metric /// * `name` - The name of the metric /// * `send_in_pings` - The pings this metric should be included in /// * `lifetime` - The lifetime of the metric (e.g., "ping", "application", "user") /// * `disabled` - Whether the metric is disabled /// * `extra_args` - Optional JSON string with additional configuration /// /// # Returns /// /// NS_OK if the metric was registered successfully, or NS_ERROR_FAILURE if registration failed #[cfg(feature = "with_gecko")] #[no_mangle] pubextern"C"fn jog_register_metric(
metric_type: &nsACString,
category: &nsACString,
name: &nsACString,
send_in_pings: &ThinVec<nsCString>,
lifetime: &nsACString,
disabled: bool,
extra_args: &nsACString,
) -> nsresult { // Validate inputs if metric_type.is_empty() || category.is_empty() || name.is_empty() || lifetime.is_empty() {
log::warn!("Failed to register metric: Missing required parameters"); return NS_ERROR_FAILURE;
}
// Convert inputs to Rust types let metric_type = metric_type.to_utf8(); let category = category.to_string(); let name = name.to_string(); let send_in_pings = send_in_pings.iter().map(|ping| ping.to_string()).collect();
// Parse lifetime with error handling let lifetime = match serde_json::from_str(&lifetime.to_utf8()) {
Ok(l) => l,
Err(e) => {
log::warn!("Failed to parse lifetime '{lifetime:?}': {e}"); return NS_ERROR_FAILURE;
}
};
// Parse extra_args with error handling let extra_args: ExtraMetricArgs = if extra_args.is_empty() {
Default::default()
} else { match serde_json::from_str(&extra_args.to_utf8()) {
Ok(args) => args,
Err(e) => {
log::warn!("Failed to parse extra_args '{extra_args:?}': {e}"); return NS_ERROR_FAILURE;
}
}
};
// Create and register the metric match create_and_register_metric(
&metric_type,
category,
name,
send_in_pings,
lifetime,
disabled,
extra_args,
) {
Ok((_, metric_id)) => {
log::debug!("Successfully registered metric with ID {}", metric_id);
NS_OK
}
Err(e) => {
log::warn!("Failed to register metric: {}", e);
NS_ERROR_FAILURE
}
}
}
/// Test-only method. /// /// Registers a ping. Doesn't check to see if it's been registered before. /// Doesn't check that it would pass schema validation if it were a real ping. #[no_mangle] pubextern"C"fn jog_test_register_ping(
name: &nsACString,
include_client_id: bool,
send_if_empty: bool,
precise_timestamps: bool,
include_info_sections: bool,
enabled: bool,
schedules_pings: &ThinVec<nsCString>,
reason_codes: &ThinVec<nsCString>,
follows_collection_enabled: bool,
uploader_capabilities: &ThinVec<nsCString>,
) -> u32 { let ping_name = name.to_string(); let reason_codes = reason_codes
.iter()
.map(|reason| reason.to_string())
.collect(); let schedules_pings = schedules_pings
.iter()
.map(|ping| ping.to_string())
.collect(); let uploader_capabilities = uploader_capabilities
.iter()
.map(|capability| capability.to_string())
.collect();
create_and_register_ping(
ping_name,
include_client_id,
send_if_empty,
precise_timestamps,
include_info_sections,
enabled,
schedules_pings,
reason_codes,
follows_collection_enabled,
uploader_capabilities,
)
.expect("Creation or registration of ping failed.") // permitted to panic in test-only method.
}
/// Creates and registers a ping as specified, /// making it and its APIs available on the JS GleanPings global. /// /// Not necessary for most uses of FOG and Glean. /// If you're not sure if you should call this, /// err on the side of not calling it. /// /// # Arguments /// /// * `name` - The name of the ping /// * `include_client_id` - Whether the ping should include the client_id /// * `send_if_empty` - Whether the ping should send even if empty /// * `precise_timestamps` - Whether to use precise timestamps /// * `include_info_sections` - Whether to include client_info and ping_info sections /// * `enabled` - Whether the ping is enabled /// * `schedules_pings` - Array of pings that this ping schedules /// * `reason_codes` - Array of valid reason codes for this ping /// * `follows_collection_enabled` - Whether this ping follows the collection enabled setting /// * `uploader_capabilities` - Array of capabilities that the uploader must support to handle this ping /// /// # Returns /// /// NS_OK if the ping was registered successfully, or NS_ERROR_FAILURE if registration failed #[no_mangle] pubextern"C"fn jog_register_ping(
name: &nsACString,
include_client_id: bool,
send_if_empty: bool,
precise_timestamps: bool,
include_info_sections: bool,
enabled: bool,
schedules_pings: &ThinVec<nsCString>,
reason_codes: &ThinVec<nsCString>,
follows_collection_enabled: bool,
uploader_capabilities: &ThinVec<nsCString>,
) -> nsresult { // Validate inputs if name.is_empty() {
log::warn!("Failed to register ping: Missing ping name"); return NS_ERROR_FAILURE;
}
// Convert inputs to Rust types let ping_name = name.to_string(); let reason_codes = reason_codes
.iter()
.map(|reason| reason.to_string())
.collect(); let schedules_pings = schedules_pings
.iter()
.map(|ping| ping.to_string())
.collect(); let uploader_capabilities = uploader_capabilities
.iter()
.map(|capability| capability.to_string())
.collect();
#[allow(clippy::too_many_arguments)] fn create_and_register_ping(
ping_name: String,
include_client_id: bool,
send_if_empty: bool,
precise_timestamps: bool,
include_info_sections: bool,
enabled: bool,
schedules_pings: Vec<String>,
reason_codes: Vec<String>,
follows_collection_enabled: bool,
uploader_capabilities: Vec<String>,
) -> Result<u32, Box<dyn std::error::Error>> { let ns_name = nsCString::from(&ping_name); let ping_id = factory::create_and_register_ping(
ping_name,
include_client_id,
send_if_empty,
precise_timestamps,
include_info_sections,
enabled,
schedules_pings,
reason_codes,
follows_collection_enabled,
uploader_capabilities,
); extern"C" { fn JOG_RegisterPing(name: &nsACString, ping_id: u32);
} iflet Ok(ping_id) = ping_id { unsafe { // Safety: We're loaning to C++ data we don't later use.
JOG_RegisterPing(&ns_name, ping_id);
}
} else {
log::warn!("Could not register ping {} due to {:?}", ns_name, ping_id);
}
ping_id
}
/// Test-only method. /// /// Clears all runtime registration storage of registered metrics and pings. /// /// **MUST BE* called from the main thread only. #[no_mangle] pubextern"C"fn jog_test_clear_registered_metrics_and_pings() {
factory::id_and_map_reset();
}
#[derive(Default, Deserialize)] struct Jogfile { // Using BTreeMap to ensure stable iteration ordering.
metrics: BTreeMap<String, Vec<MetricDefinitionData>>,
pings: Vec<PingDefinitionData>,
}
/// Read the file at the provided location, interpret it as a jogfile, /// and register those pings and metrics. /// Returns true if we successfully parsed the jogfile. Does not mean /// all or any metrics and pings successfully registered, /// just that serde managed to deserialize it into metrics and pings and we tried to register them all. #[no_mangle] pubextern"C"fn jog_load_jogfile(jogfile_path: &nsAString) -> bool { let f = match File::open(jogfile_path.to_string()) {
Ok(f) => f,
_ => {
log::error!("Boo, couldn't open jogfile at {}", jogfile_path.to_string()); returnfalse;
}
}; let reader = BufReader::new(f);
let j: Jogfile = match serde_json::from_reader(reader) {
Ok(j) => j,
Err(e) => {
log::error!("Boo, couldn't read jogfile because of: {:?}", e); returnfalse;
}
};
jog_register_jogfile(j)
}
/// Parse the given string as though it were a jogfile's contents /// and register those pings and metrics. /// Returns true if we successfully parsed the jogfile. Does not mean /// all or any metrics and pings successfully registered, /// just that serde managed to deserialize it into metrics and pings and we tried to register them all. #[no_mangle] pubextern"C"fn jog_load_jogfile_str(jogfile_contents: &nsCString) -> bool { let j: Jogfile = match serde_json::from_str(&jogfile_contents.to_utf8()) {
Ok(j) => j,
Err(e) => {
log::error!("Boo, couldn't parse jogfile contents because of: {:?}", e); returnfalse;
}
};
jog_register_jogfile(j)
}
fn jog_register_jogfile(j: Jogfile) -> bool {
log::trace!("Loaded jogfile. Registering metrics+pings."); for (category, metrics) in j.metrics.into_iter() { for metric in metrics.into_iter() { let _ = create_and_register_metric(
&metric.metric_type,
category.to_string(),
metric.name,
metric.send_in_pings,
metric.lifetime,
metric.disabled,
metric.extra_args.unwrap_or_else(Default::default),
);
}
} for ping in j.pings.into_iter() { let _ = create_and_register_ping(
ping.name,
ping.include_client_id,
ping.send_if_empty,
ping.precise_timestamps,
ping.include_info_sections,
ping.enabled,
ping.schedules_pings.unwrap_or_else(Vec::new),
ping.reason_codes.unwrap_or_else(Vec::new),
ping.follows_collection_enabled,
ping.uploader_capabilities,
);
} true
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.