1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
// 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/.
use std::ops::Deref;
use std::sync::atomic::{AtomicU8, Ordering};
use malloc_size_of_derive::MallocSizeOf;
use crate::error::{Error, ErrorKind};
use crate::metrics::dual_labeled_counter::validate_dynamic_key_and_or_category;
use crate::metrics::labeled::validate_dynamic_label;
use crate::Glean;
use serde::{Deserialize, Serialize};
/// The supported metrics' lifetimes.
///
/// A metric's lifetime determines when its stored data gets reset.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize, Serialize, Default, MallocSizeOf)]
#[repr(i32)] // Use i32 to be compatible with our JNA definition
#[serde(rename_all = "lowercase")]
pub enum Lifetime {
/// The metric is reset with each sent ping
#[default]
Ping,
/// The metric is reset on application restart
Application,
/// The metric is reset with each user profile
User,
}
impl Lifetime {
/// String representation of the lifetime.
pub fn as_str(self) -> &'static str {
match self {
Lifetime::Ping => "ping",
Lifetime::Application => "app",
Lifetime::User => "user",
}
}
}
impl TryFrom<i32> for Lifetime {
type Error = Error;
fn try_from(value: i32) -> Result<Lifetime, Self::Error> {
match value {
0 => Ok(Lifetime::Ping),
1 => Ok(Lifetime::Application),
2 => Ok(Lifetime::User),
e => Err(ErrorKind::Lifetime(e).into()),
}
}
}
/// The common set of data shared across all different metric types.
#[derive(Default, Debug, Clone, Deserialize, Serialize, MallocSizeOf)]
pub struct CommonMetricData {
/// The metric's name.
pub name: String,
/// The metric's category.
pub category: String,
/// List of ping names to include this metric in.
pub send_in_pings: Vec<String>,
/// The metric's lifetime.
pub lifetime: Lifetime,
/// Whether or not the metric is disabled.
///
/// Disabled metrics are never recorded.
pub disabled: bool,
/// Dynamic label.
///
/// When a [`LabeledMetric<T>`](crate::metrics::LabeledMetric) factory creates the specific
/// metric to be recorded to, dynamic labels are stored in the specific
/// label so that we can validate them when the Glean singleton is
/// available.
pub dynamic_label: Option<DynamicLabelType>,
}
/// The type of dynamic label applied to a base metric. Used to help identify
/// the necessary validation to be performed.
#[derive(Debug, Clone, Deserialize, Serialize, MallocSizeOf, uniffi::Enum)]
pub enum DynamicLabelType {
/// A dynamic label applied from a `LabeledMetric`
Label(String),
/// A label applied by a `DualLabeledCounter` that contains a dynamic key
KeyOnly(String),
/// A label applied by a `DualLabeledCounter` that contains a dynamic category
CategoryOnly(String),
/// A label applied by a `DualLabeledCounter` that contains a dynamic key and category
KeyAndCategory(String),
}
impl Default for DynamicLabelType {
fn default() -> Self {
Self::Label(String::new())
}
}
impl Deref for DynamicLabelType {
type Target = str;
fn deref(&self) -> &Self::Target {
match self {
DynamicLabelType::Label(label) => label,
DynamicLabelType::KeyOnly(key) => key,
DynamicLabelType::CategoryOnly(category) => category,
DynamicLabelType::KeyAndCategory(key_and_category) => key_and_category,
}
}
}
#[derive(Default, Debug, MallocSizeOf)]
pub struct CommonMetricDataInternal {
pub inner: CommonMetricData,
pub disabled: AtomicU8,
}
impl Clone for CommonMetricDataInternal {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
disabled: AtomicU8::new(self.disabled.load(Ordering::Relaxed)),
}
}
}
impl From<CommonMetricData> for CommonMetricDataInternal {
fn from(input_data: CommonMetricData) -> Self {
let disabled = input_data.disabled;
Self {
inner: input_data,
disabled: AtomicU8::new(u8::from(disabled)),
}
}
}
impl CommonMetricDataInternal {
/// Creates a new metadata object.
pub fn new<A: Into<String>, B: Into<String>, C: Into<String>>(
category: A,
name: B,
ping_name: C,
) -> CommonMetricDataInternal {
CommonMetricDataInternal {
inner: CommonMetricData {
name: name.into(),
category: category.into(),
send_in_pings: vec![ping_name.into()],
..Default::default()
},
disabled: AtomicU8::new(0),
}
}
/// The metric's base identifier, including the category and name, but not the label.
///
/// If `category` is empty, it's ommitted.
/// Otherwise, it's the combination of the metric's `category` and `name`.
pub(crate) fn base_identifier(&self) -> String {
if self.inner.category.is_empty() {
self.inner.name.clone()
} else {
format!("{}.{}", self.inner.category, self.inner.name)
}
}
/// The metric's unique identifier, including the category, name and label.
///
/// If `category` is empty, it's ommitted.
/// Otherwise, it's the combination of the metric's `category`, `name` and `label`.
pub(crate) fn identifier(&self, glean: &Glean) -> String {
let base_identifier = self.base_identifier();
if let Some(label) = &self.inner.dynamic_label {
match label {
DynamicLabelType::Label(label) => {
validate_dynamic_label(glean, self, &base_identifier, label)
}
_ => validate_dynamic_key_and_or_category(
glean,
self,
&base_identifier,
label.clone(),
),
}
} else {
base_identifier
}
}
/// The list of storages this metric should be recorded into.
pub fn storage_names(&self) -> &[String] {
&self.inner.send_in_pings
}
}