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
// 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::sync::atomic::{AtomicU8, Ordering};
use crate::error::{Error, ErrorKind};
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)]
#[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)]
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<String>,
}
#[derive(Default, Debug)]
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 {
validate_dynamic_label(glean, self, &base_identifier, label)
} else {
base_identifier
}
}
/// The list of storages this metric should be recorded into.
pub fn storage_names(&self) -> &[String] {
&self.inner.send_in_pings
}
}