// 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::borrow::Cow;
use malloc_size_of_derive::MallocSizeOf;
use serde::Serialize;
use super::{metrics::*, CommonMetricData, LabeledMetricData, Lifetime};
#[derive(Debug, MallocSizeOf)]
pub struct CoreMetrics {
pub client_id: UuidMetric,
pub first_run_date: DatetimeMetric,
pub os: StringMetric,
pub attribution_source: StringMetric,
pub attribution_medium: StringMetric,
pub attribution_campaign: StringMetric,
pub attribution_term: StringMetric,
pub attribution_content: StringMetric,
pub distribution_name: StringMetric,
}
#[derive(Debug, MallocSizeOf)]
pub struct AdditionalMetrics {
/// The number of times we encountered an IO error
/// when writing a pending ping to disk.
pub io_errors: CounterMetric,
/// A count of the pings submitted, by ping type.
pub pings_submitted: LabeledMetric<CounterMetric>,
/// Time waited for the uploader at shutdown.
pub shutdown_wait: TimingDistributionMetric,
/// Time waited for the dispatcher to unblock during shutdown.
pub shutdown_dispatcher_wait: TimingDistributionMetric,
/// An experimentation identifier derived and provided by the application
/// for the purpose of experimentation enrollment.
pub experimentation_id: StringMetric,
/// The number of times we had to clamp an event timestamp
/// for exceeding the range of a signed 64-bit integer (9223372036854775807).
pub event_timestamp_clamped: CounterMetric,
/// Server knobs configuration received from remote settings.
pub server_knobs_config: ObjectMetric,
}
impl CoreMetrics {
pub fn new() -> CoreMetrics {
CoreMetrics {
client_id: UuidMetric::new(CommonMetricData {
name:
"client_id".into(),
category:
"".into(),
send_in_pings: vec![
"glean_client_info".into()],
lifetime: Lifetime::User,
disabled:
false,
dynamic_label: None,
}),
first_run_date: DatetimeMetric::new(
CommonMetricData {
name:
"first_run_date".into(),
category:
"".into(),
send_in_pings: vec![
"glean_client_info".into()],
lifetime: Lifetime::User,
disabled:
false,
dynamic_label: None,
},
TimeUnit::Day,
),
os: StringMetric::new(CommonMetricData {
name:
"os".into(),
category:
"".into(),
send_in_pings: vec![
"glean_client_info".into()],
lifetime: Lifetime::Application,
disabled:
false,
dynamic_label: None,
}),
attribution_source: StringMetric::new(CommonMetricData {
name:
"source".into(),
category:
"attribution".into(),
send_in_pings: vec![
"glean_client_info".into()],
lifetime: Lifetime::User,
disabled:
false,
dynamic_label: None,
}),
attribution_medium: StringMetric::new(CommonMetricData {
name:
"medium".into(),
category:
"attribution".into(),
send_in_pings: vec![
"glean_client_info".into()],
lifetime: Lifetime::User,
disabled:
false,
dynamic_label: None,
}),
attribution_campaign: StringMetric::new(CommonMetricData {
name:
"campaign".into(),
category:
"attribution".into(),
send_in_pings: vec![
"glean_client_info".into()],
lifetime: Lifetime::User,
disabled:
false,
dynamic_label: None,
}),
attribution_term: StringMetric::new(CommonMetricData {
name:
"term".into(),
category:
"attribution".into(),
send_in_pings: vec![
"glean_client_info".into()],
lifetime: Lifetime::User,
disabled:
false,
dynamic_label: None,
}),
attribution_content: StringMetric::new(CommonMetricData {
name:
"content".into(),
category:
"attribution".into(),
send_in_pings: vec![
"glean_client_info".into()],
lifetime: Lifetime::User,
disabled:
false,
dynamic_label: None,
}),
distribution_name: StringMetric::new(CommonMetricData {
name:
"name".into(),
category:
"distribution".into(),
send_in_pings: vec![
"glean_client_info".into()],
lifetime: Lifetime::User,
disabled:
false,
dynamic_label: None,
}),
}
}
}
impl AdditionalMetrics {
pub fn new() -> AdditionalMetrics {
AdditionalMetrics {
io_errors: CounterMetric::new(CommonMetricData {
name:
"io".into(),
category:
"glean.error".into(),
send_in_pings: vec![
"metrics".into(),
"health".into()],
lifetime: Lifetime::Ping,
disabled:
false,
dynamic_label: None,
}),
pings_submitted: LabeledMetric::<CounterMetric>::new(
LabeledMetricData::Common {
cmd: CommonMetricData {
name:
"pings_submitted".into(),
category:
"glean.validation".into(),
send_in_pings: vec![
"metrics".into(),
"baseline".into(),
"health".into()],
lifetime: Lifetime::Ping,
disabled:
false,
dynamic_label: None,
},
},
None,
),
shutdown_wait: TimingDistributionMetric::new(
CommonMetricData {
name:
"shutdown_wait".into(),
category:
"glean.validation".into(),
send_in_pings: vec![
"metrics".into(),
"health".into()],
lifetime: Lifetime::Ping,
disabled:
false,
dynamic_label: None,
},
TimeUnit::Millisecond,
),
shutdown_dispatcher_wait: TimingDistributionMetric::new(
CommonMetricData {
name:
"shutdown_dispatcher_wait".into(),
category:
"glean.validation".into(),
send_in_pings: vec![
"metrics".into(),
"health".into()],
lifetime: Lifetime::Ping,
disabled:
false,
dynamic_label: None,
},
TimeUnit::Millisecond,
),
// This uses a `send_in_pings` that contains "all-ping".
// This works because all of our other current "all-pings" metrics
// have special handling internally and are not actually processed
// into a store quite like this identifier is.
//
// This could become an issue if we ever decide to start generating
// code from the internal Glean metrics.yaml (there aren't currently
// any plans for this).
experimentation_id: StringMetric::new(CommonMetricData {
name:
"experimentation_id".into(),
category:
"glean.client.annotation".into(),
send_in_pings: vec![
"all-pings".into()],
lifetime: Lifetime::Application,
disabled:
false,
dynamic_label: None,
}),
event_timestamp_clamped: CounterMetric::new(CommonMetricData {
name:
"event_timestamp_clamped".into(),
category:
"glean.error".into(),
send_in_pings: vec![
"health".into()],
lifetime: Lifetime::Ping,
disabled:
false,
dynamic_label: None,
}),
server_knobs_config: ObjectMetric::new(CommonMetricData {
name:
"server_knobs_config".into(),
category:
"glean.internal.metrics".into(),
send_in_pings: vec![
"glean_internal_info".into()],
lifetime: Lifetime::Application,
disabled:
false,
dynamic_label: None,
}),
}
}
}
#[derive(Debug, MallocSizeOf)]
pub struct UploadMetrics {
pub ping_upload_failure: LabeledMetric<CounterMetric>,
pub discarded_exceeding_pings_size: MemoryDistributionMetric,
pub pending_pings_directory_size: MemoryDistributionMetric,
pub deleted_pings_after_quota_hit: CounterMetric,
pub pending_pings: CounterMetric,
pub send_success: TimingDistributionMetric,
pub send_failure: TimingDistributionMetric,
pub in_flight_pings_dropped: CounterMetric,
pub missing_send_ids: CounterMetric,
}
impl UploadMetrics {
pub fn new() -> UploadMetrics {
UploadMetrics {
ping_upload_failure: LabeledMetric::<CounterMetric>::new(
LabeledMetricData::Common {
cmd: CommonMetricData {
name:
"ping_upload_failure".into(),
category:
"glean.upload".into(),
send_in_pings: vec![
"metrics".into(),
"health".into()],
lifetime: Lifetime::Ping,
disabled:
false,
dynamic_label: None,
},
},
Some(vec![
Cow::from(
"status_code_4xx"),
Cow::from(
"status_code_5xx"),
Cow::from(
"status_code_unknown"),
Cow::from(
"unrecoverable"),
Cow::from(
"recoverable"),
Cow::from(
"incapable"),
]),
),
discarded_exceeding_pings_size: MemoryDistributionMetric::new(
CommonMetricData {
name:
"discarded_exceeding_pings_size".into(),
category:
"glean.upload".into(),
send_in_pings: vec![
"metrics".into(),
"health".into()],
lifetime: Lifetime::Ping,
disabled:
false,
dynamic_label: None,
},
MemoryUnit::Kilobyte,
),
pending_pings_directory_size: MemoryDistributionMetric::new(
CommonMetricData {
name:
"pending_pings_directory_size".into(),
category:
"glean.upload".into(),
send_in_pings: vec![
"metrics".into(),
"health".into()],
lifetime: Lifetime::Ping,
disabled:
false,
dynamic_label: None,
},
MemoryUnit::Kilobyte,
),
deleted_pings_after_quota_hit: CounterMetric::new(CommonMetricData {
name:
"deleted_pings_after_quota_hit".into(),
category:
"glean.upload".into(),
send_in_pings: vec![
"metrics".into(),
"health".into()],
lifetime: Lifetime::Ping,
disabled:
false,
dynamic_label: None,
}),
pending_pings: CounterMetric::new(CommonMetricData {
name:
"pending_pings".into(),
category:
"glean.upload".into(),
send_in_pings: vec![
"metrics".into(),
"health".into()],
lifetime: Lifetime::Ping,
disabled:
false,
dynamic_label: None,
}),
send_success: TimingDistributionMetric::new(
CommonMetricData {
name:
"send_success".into(),
category:
"glean.upload".into(),
send_in_pings: vec![
"metrics".into(),
"health".into()],
lifetime: Lifetime::Ping,
disabled:
false,
dynamic_label: None,
},
TimeUnit::Millisecond,
),
send_failure: TimingDistributionMetric::new(
CommonMetricData {
name:
"send_failure".into(),
category:
"glean.upload".into(),
send_in_pings: vec![
"metrics".into(),
"health".into()],
lifetime: Lifetime::Ping,
disabled:
false,
dynamic_label: None,
},
TimeUnit::Millisecond,
),
in_flight_pings_dropped: CounterMetric::new(CommonMetricData {
name:
"in_flight_pings_dropped".into(),
category:
"glean.upload".into(),
send_in_pings: vec![
"metrics".into(),
"health".into()],
lifetime: Lifetime::Ping,
disabled:
false,
dynamic_label: None,
}),
missing_send_ids: CounterMetric::new(CommonMetricData {
name:
"missing_send_ids".into(),
category:
"glean.upload".into(),
send_in_pings: vec![
"metrics".into(),
"health".into()],
lifetime: Lifetime::Ping,
disabled:
false,
dynamic_label: None,
}),
}
}
}
#[derive(Debug, MallocSizeOf)]
pub struct DatabaseMetrics {
pub size: MemoryDistributionMetric,
/// RKV's load result, indicating success or relaying the detected error.
pub rkv_load_error: StringMetric,
/// The time it takes for a write-commit for the Glean database.
pub write_time: TimingDistributionMetric,
}
impl DatabaseMetrics {
pub fn new() -> DatabaseMetrics {
DatabaseMetrics {
size: MemoryDistributionMetric::new(
CommonMetricData {
name:
"size".into(),
category:
"glean.database".into(),
send_in_pings: vec![
"metrics".into(),
"health".into()],
lifetime: Lifetime::Ping,
disabled:
false,
dynamic_label: None,
},
MemoryUnit::Byte,
),
rkv_load_error: StringMetric::new(CommonMetricData {
name:
"rkv_load_error".into(),
category:
"glean.database".into(),
send_in_pings: vec![
"metrics".into(),
"health".into()],
lifetime: Lifetime::Ping,
disabled:
false,
dynamic_label: None,
}),
write_time: TimingDistributionMetric::new(
CommonMetricData {
name:
"write_time".into(),
category:
"glean.database".into(),
send_in_pings: vec![
"metrics".into(),
"health".into()],
lifetime: Lifetime::Ping,
disabled:
true,
dynamic_label: None,
},
TimeUnit::Microsecond,
),
}
}
}
/// Possible values for the `glean.health.exception_state` health metric.
pub enum ExceptionState {
/// No database on disk, but the plaintext file contained a valid client ID.
EmptyDb,
/// Existing database, but no client ID, however a client ID in the plaintext file.
RegenDb,
/// The database contained a c0ffee client ID.
C0ffeeInDb,
/// The client IDs in the database and the plaintext file differ.
ClientIdMismatch,
}
impl From<ExceptionState>
for String {
fn from(value: ExceptionState) ->
Self {
use ExceptionState::*;
String::from(
match value {
EmptyDb =>
"empty-db",
RegenDb =>
"regen-db",
C0ffeeInDb =>
"c0ffee-in-db",
ClientIdMismatch =>
"client-id-mismatch",
})
}
}
#[derive(Debug, MallocSizeOf)]
pub struct HealthMetrics {
// Information about the data directory prior to Glean initialization.
pub data_directory_info: ObjectMetric,
// A running count of the number of initializations.
pub init_count: CounterMetric,
// An exceptional state was detected upon trying to laod the database.
pub exception_state: StringMetric,
// A client_id recovered from a `client_id.txt` file on disk.
pub recovered_client_id: UuidMetric,
pub file_read_error: LabeledCounter,
pub file_write_error: LabeledCounter,
}
impl HealthMetrics {
pub fn new() -> HealthMetrics {
HealthMetrics {
data_directory_info: ObjectMetric::new(CommonMetricData {
name:
"data_directory_info".into(),
category:
"glean.health".into(),
send_in_pings: vec![
"metrics".into(),
"health".into()],
lifetime: Lifetime::Ping,
disabled:
false,
dynamic_label: None,
}),
init_count: CounterMetric::new(CommonMetricData {
name:
"init_count".into(),
category:
"glean.health".into(),
send_in_pings: vec![
"health".into()],
lifetime: Lifetime::User,
disabled:
false,
dynamic_label: None,
}),
exception_state: StringMetric::new(CommonMetricData {
name:
"exception_state".into(),
category:
"glean.health".into(),
send_in_pings: vec![
"health".into()],
lifetime: Lifetime::Ping,
disabled:
false,
dynamic_label: None,
}),
recovered_client_id: UuidMetric::new(CommonMetricData {
name:
"recovered_client_id".into(),
category:
"glean.health".into(),
send_in_pings: vec![
"health".into()],
lifetime: Lifetime::Ping,
disabled:
false,
dynamic_label: None,
}),
file_read_error: LabeledMetric::<CounterMetric>::new(
LabeledMetricData::Common {
cmd: CommonMetricData {
name:
"file_read_error".into(),
category:
"glean.health".into(),
send_in_pings: vec![
"health".into()],
lifetime: Lifetime::Ping,
disabled:
false,
dynamic_label: None,
},
},
Some(vec![
Cow::from(
"parse"),
Cow::from(
"permission-denied"),
Cow::from(
"io"),
Cow::from(
"c0ffee-in-file"),
Cow::from(
"file-not-found"),
]),
),
file_write_error: LabeledMetric::<CounterMetric>::new(
LabeledMetricData::Common {
cmd: CommonMetricData {
name:
"file_write_error".into(),
category:
"glean.health".into(),
send_in_pings: vec![
"health".into()],
lifetime: Lifetime::Ping,
disabled:
false,
dynamic_label: None,
},
},
Some(vec![
Cow::from(
"not-found"),
Cow::from(
"permission-denied"),
Cow::from(
"io"),
]),
),
}
}
}
pub type DataDirectoryInfoObject = Vec<DataDirectoryInfoObjectItem>;
#[derive(Debug, Serialize)]
#[serde(deny_unknown_fields)]
pub struct DataDirectoryInfoObjectItem {
#[serde(skip_serializing_if =
"Option::is_none")]
pub dir_name: Option<String>,
#[serde(skip_serializing_if =
"Option::is_none")]
pub dir_exists: Option<bool>,
#[serde(skip_serializing_if =
"Option::is_none")]
pub dir_created: Option<i64>,
#[serde(skip_serializing_if =
"Option::is_none")]
pub dir_modified: Option<i64>,
#[serde(skip_serializing_if =
"Option::is_none")]
pub file_count: Option<i64>,
#[serde(skip_serializing_if =
"Option::is_none")]
pub error_message: Option<String>,
#[serde(skip_serializing_if =
"Vec::is_empty", default =
"Vec::new")]
pub files: DataDirectoryInfoObjectItemItemFiles,
}
pub type DataDirectoryInfoObjectItemItemFiles = Vec<DataDirectoryInfoObjectItemItemFi
lesItem>;
#[derive(Debug, Serialize)]
#[serde(deny_unknown_fields)]
pub struct DataDirectoryInfoObjectItemItemFilesItem {
#[serde(skip_serializing_if = "Option::is_none")]
pub file_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub file_created: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub file_modified: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub file_size: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error_message: Option<String>,
}