// 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 different metric types supported by the Glean SDK to handle data.
// Re-export of `glean` types we can re-use. // That way a user only needs to depend on this crate, not on glean (and there can't be a // version mismatch). pubuse glean::{
traits, CommonMetricData, DistributionData, ErrorType, LabeledMetricData, Lifetime, MemoryUnit,
RecordedEvent, TimeUnit, TimerId,
};
#[macro_use] mod metric_getter;
mod boolean; mod counter; mod custom_distribution; mod datetime; mod denominator; mod dual_labeled_counter; mod dual_labeled_counter_sub; mod event; mod labeled; mod labeled_boolean; mod labeled_counter; mod labeled_custom_distribution; mod labeled_memory_distribution; mod labeled_timing_distribution; mod memory_distribution; mod numerator; mod object; mod ping; mod quantity; mod rate; pub(crate) mod string; mod string_list; mod text; mod timespan; mod timing_distribution; mod url; mod uuid;
/// A metadata structure common to all Child metrics. Storing this data is not /// necessary at all times, but many metrics require the ID for IPC /// operations, and storing the name + category is necessary so that profiler /// markers can be accurately recorded in child processes. #[derive(Debug, Clone)] pubstruct ChildMetricMeta { pub id: BaseMetricId, pub name: String, pub category: String,
}
// We only access the methods here when we're building with Gecko, as that's // when we have access to the profiler. We don't need alternative (i.e. // non-gecko) implementations, as any imports from this sub-module are also // gated with the same #[cfg(feature...)] #[cfg(feature = "with_gecko")] pub(crate) mod profiler_utils { use std::marker::PhantomData;
// Declare the telemetry profiling category as a constant here. // This lets us avoid re-importing gecko_profiler ... within metric files, // which keeps the importing a bit cleaner, and reduces profiler intrusion. #[allow(non_upper_case_globals)] pubconst TelemetryProfilerCategory: gecko_profiler::ProfilingCategoryPair =
gecko_profiler::ProfilingCategoryPair::Telemetry(None);
pub(crate) fn local_now_with_offset() -> DateTime<FixedOffset> { // See https://bugzilla.mozilla.org/show_bug.cgi?id=1611770. // // It's not clear if this bug on Windows still exist with the latest versions of // the `time` crate. Removed the workaround. let now: DateTime<Local> = Local::now();
now.with_timezone(now.offset())
}
/// Try to convert a glean::Datetime into a chrono::DateTime Returns none if /// the glean::Datetime offset is not a valid timezone We would prefer to /// use .into or similar, but we need to wait until this is implemented in /// the Glean SDK. See Bug 1925313 for more details. #[allow(deprecated)] // use of deprecated chrono functions. pub(crate) fn glean_to_chrono_datetime(
gdt: &glean::Datetime,
) -> Option<chrono::LocalResult<chrono::DateTime<chrono::FixedOffset>>> { use chrono::{FixedOffset, TimeZone}; let tz = FixedOffset::east_opt(gdt.offset_seconds); if tz.is_none() { return None;
}
// Truncate a vector down to a maximum size. // We want to avoid storing large vectors of values in the profiler buffer, // so this helper method allows markers to explicitly limit the size of // vectors of values that might originate from Glean pub(crate) fn truncate_vector_for_marker<T>(vec: &Vec<T>) -> Vec<T> where
T: Clone,
{ const MAX_VECTOR_LENGTH: usize = 1024; if vec.len() > MAX_VECTOR_LENGTH {
vec[0..MAX_VECTOR_LENGTH - 1].to_vec()
} else {
vec.clone()
}
}
// Given an ID, look up the metric referred to by said ID, get the // metadata associated with it (category, name, label), and stream that // information (with the JSONWriter) into the profiler buffer. pub(crate) fn stream_identifiers_by_id<MetricT: MetricMetadataGetter + MetricNamer>(
id: &super::MetricId,
json_writer: &mut gecko_profiler::JSONWriter,
) { match MetricT::get_metric_metadata_by_id(id) {
Ok((metadata, mlabel)) => { // Write the category, as that will always be contained // (correctly) within the metadata that we extract from // within the metric.
json_writer.unique_string_property("cat", &metadata.category); // Getting the name and label of a metric is non trivial. We // have three different (potential) sources of label, one of // which requires us to split the name to get it, so do some // case by case analysis to try and get a coherent // name/label. match metadata.label.or(mlabel) { // If either the label we get from the metric, or the // label we got while retrieveing the metric, is valid, // use them directly.
Some(label) => { // If the label comes from a dual_labeled_counter, // it is prefixed and delimited with a record separator // which we should adapt for display. let label = label.strip_prefix('\x1E').unwrap_or(&label); let label = label.replace('\x1E', ", ");
json_writer.unique_string_property("id", &metadata.name);
json_writer.unique_string_property("label", &label);
} // Otherwise, assume that the label must be part of the // name, and try and split it.
None => match metadata.name.split_once("/") {
Some((name, label)) => {
json_writer.unique_string_property("id", &name);
json_writer.unique_string_property("label", &label);
}
None => {
json_writer.unique_string_property("id", &metadata.name);
}
},
}
}
Err(e) => { let error_string = format!("Error looking up {:?}: {:?}", id, e);
json_writer.unique_string_property("id", &error_string);
}
}
}
// This might seem like overkill for discerning between a single element and // a vector of elements. However, from the perspective of the profiler buffer // this is quite reasonable, as it has a lower memory overhead. Doing the maths // (and assuming a 64-bit system, so usize = 8 bytes): // Enum: i64 value (8-bytes), enum discernment byte = 9 bytes, // Vector: i64 values (at least 8 bytes), usize length, usize capacity, data // pointer = 32 bytes #[derive(serde::Serialize, serde::Deserialize, Debug)] pub(crate) enum DistributionValues<T> {
Sample(T),
Samples(Vec<T>),
}
// These two methods, and the constant function, "live" within profiler_utils, // but as we need them available for testing, when we might not have gecko // available, we use a different set of cfg features to enable them in both // cases. Note that we re-export the main truncation method within // `profiler_utils` to correct the namespace. #[cfg(any(feature = "with_gecko", test))] pub(crate) fn truncate_string_for_marker(input: String) -> String {
truncate_string_for_marker_to_length(input, max_string_byte_length())
}
#[cfg(any(feature = "with_gecko", test))] #[inline] fn truncate_string_for_marker_to_length(mut input: String, byte_length: usize) -> String { // Truncating an arbitrary string in Rust is not not exactly easy, as // Strings are UTF-8 encoded. The "built-in" String::truncate, however, // operates on bytes, and panics if the truncation crosses a character // boundary. // To avoid this, we need to find the first unicode char boundary that // is less than the size that we're looking for. Note that we're // interested in how many *bytes* the string takes up (when we add it // to a marker), so we truncate to `MAX_STRING_BYTE_LENGTH` bytes, or // (by walking the truncation point back) to a number of bytes that // still represents valid UTF-8. // Note, this truncation may not provide a valid json result, and // truncation acts on glyphs, not graphemes, so the resulting text // may not render exactly the same as before it was truncated.
// Copied from src/core/num/mod.rs // Check if a given byte is a utf8 character boundary #[inline] constfn is_utf8_char_boundary(b: u8) -> bool { // This is bit magic equivalent to: b < 128 || b >= 192
(b as i8) >= -0x40
}
// Check if our truncation point is a char boundary. If it isn't, move // it "back" along the string until it is. // Note, this is an almost direct port of the rust standard library // function `str::floor_char_boundary`. We re-produce it as this API is // not yet stable, and we make some small changes (such as modifying // the input in-place) that are more convenient for this method. if byte_length < input.len() { let lower_bound = byte_length.saturating_sub(3);
let new_byte_length = input.as_bytes()[lower_bound..=byte_length]
.iter()
.rposition(|b| is_utf8_char_boundary(*b));
// SAFETY: we know that the character boundary will be within four bytes let truncation_point = unsafe { lower_bound + new_byte_length.unwrap_unchecked() };
input.truncate(truncation_point)
}
input
}
pubtrait TestGetNumErrors { /// **Exported for test purposes.** /// /// Gets the number of recorded errors for the given metric and error type. /// /// # Arguments /// /// * `error` - The type of error /// /// # Returns /// /// The number of errors reported. fn test_get_num_recorded_errors(&self, error_type: glean::ErrorType) -> i32;
}
#[cfg(test)] mod truncation_tests { usecrate::private::truncate_string_for_marker; usecrate::private::truncate_string_for_marker_to_length;
// Check a series of truncation points (i.e. string lengths), and assert // that they all produce the same trunctated string from the input. fn check_many(s: &str, arg: impl IntoIterator<Item = usize>, truncated: &str) { for len in arg {
assert_eq!(
truncate_string_for_marker_to_length(s.to_string(), len),
truncated, "truncate_string_for_marker_to_length({:?}, {:?}) != {:?}",
len,
s,
truncated
);
}
}
// Check a single string against it's expected truncated outcome fn check_one(s: String, truncated: String) {
assert_eq!(
truncate_string_for_marker(s.clone()),
truncated, "truncate_string_for_marker({:?}) != {:?}",
s,
truncated
);
}
#[test] fn full_truncation() { // Keep the values in this up to date with MAX_STRING_BYTE_LENGTH
// For each of these tests, we use a padding value to get near to 1024 // bytes, then add on a variety of further characters that push us up // to or over the limit. We then check that we correctly truncated to // the correct character or grapheme. let pad = |reps: usize| -> String { "-".repeat(reps) };
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.