diff --git a/tools/generate-rust-dashboards/src/config.rs b/tools/generate-rust-dashboards/src/config.rs index f4703fc544..0f2e75f1c0 100644 --- a/tools/generate-rust-dashboards/src/config.rs +++ b/tools/generate-rust-dashboards/src/config.rs @@ -52,6 +52,7 @@ pub enum Metric { LabeledCounter(LabeledCounterMetric), Distribution(DistributionMetric), LabeledDistribution(LabeledDistributionMetric), + Events(EventsMetric), } /// Glean counter @@ -154,6 +155,22 @@ pub enum DistributionMetricKind { Custom, } +/// Track multiple Glean events together +/// +/// This will create time-series panels with event counts for each event +pub struct EventsMetric { + /// Name to display on the dashboard + pub display_name: &'static str, + /// Name of the ping ("metrics" by default) + pub ping: &'static str, + /// Category name (top-level key in metrics.yaml) + pub category: &'static str, + /// Metric name (key for the metric) + pub metrics: Vec<&'static str>, + // Which applications report this metric + pub applications: Vec, +} + #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum Application { Android, @@ -263,3 +280,9 @@ impl From for Metric { Self::LabeledDistribution(m) } } + +impl From for Metric { + fn from(m: EventsMetric) -> Self { + Self::Events(m) + } +} diff --git a/tools/generate-rust-dashboards/src/metrics/event.rs b/tools/generate-rust-dashboards/src/metrics/event.rs new file mode 100644 index 0000000000..d8f4908061 --- /dev/null +++ b/tools/generate-rust-dashboards/src/metrics/event.rs @@ -0,0 +1,87 @@ +/* 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 http://mozilla.org/MPL/2.0/. */ + +use crate::{ + config::{Application, EventsMetric, ReleaseChannel, TeamConfig}, + schema::{ + DashboardBuilder, Datasource, FieldConfig, FieldConfigCustom, FieldConfigDefaults, GridPos, + Panel, Target, TimeSeriesPanel, Transformation, + }, + sql::{Query, Union}, + Result, +}; + +pub fn add_to_dashboard( + builder: &mut DashboardBuilder, + _config: &TeamConfig, + metric: &EventsMetric, +) -> Result<()> { + builder.add_panel_title(metric.display_name); + for app in metric.applications.iter().cloned() { + builder.add_panel_third(count_panel(app, ReleaseChannel::Nightly, metric)); + builder.add_panel_third(count_panel(app, ReleaseChannel::Beta, metric)); + builder.add_panel_third(count_panel(app, ReleaseChannel::Release, metric)); + } + Ok(()) +} + +fn count_panel(application: Application, channel: ReleaseChannel, metric: &EventsMetric) -> Panel { + let EventsMetric { + ping, + category, + metrics, + .. + } = metric; + + let mut query = Union::default(); + for metric in metrics { + query.queries.push(Query { + select: vec![ + "TIMESTAMP(submission_date) as time".into(), + format!("'{metric}' as label"), + "SUM(count) as count".into(), + ], + from: format!("`mozdata.rust_components.{ping}_{category}_{metric}`"), + where_: vec![ + "$__timeFilter(TIMESTAMP(submission_date))".into(), + format!("application = '{}'", application.slug()), + format!("channel = '{channel}'"), + ], + group_by: Some("1, 2".into()), + ..Query::default() + }); + } + query.order_by = Some("submission_date asc".into()); + + TimeSeriesPanel { + title: application.display_name(channel), + grid_pos: GridPos::height(8), + datasource: Datasource::bigquery(), + interval: "1d".into(), + targets: vec![Target::table(query.sql())], + field_config: FieldConfig { + defaults: FieldConfigDefaults { + links: vec![], + custom: FieldConfigCustom { + axis_label: "count / day".into(), + ..FieldConfigCustom::default() + }, + unit: None, + }, + }, + transformations: vec![ + Transformation::PartitionByValues { + fields: vec!["label".into()], + keep_fields: true, + }, + // Fixup the field names for better legend labels + Transformation::RenameByRegex { + regex: "count (.*)".into(), + rename_pattern: "$1".into(), + }, + ], + ..TimeSeriesPanel::default() + } + .into() +} diff --git a/tools/generate-rust-dashboards/src/metrics/mod.rs b/tools/generate-rust-dashboards/src/metrics/mod.rs index 451a775502..72c2540091 100644 --- a/tools/generate-rust-dashboards/src/metrics/mod.rs +++ b/tools/generate-rust-dashboards/src/metrics/mod.rs @@ -4,6 +4,7 @@ pub mod counter; pub mod distribution; +pub mod event; pub mod labeled_counter; pub mod labeled_distribution; pub mod rust_component_errors; @@ -30,6 +31,7 @@ impl Metric { Self::LabeledDistribution(metric) => { labeled_distribution::add_to_dashboard(builder, config, metric) } + Self::Events(metric) => event::add_to_dashboard(builder, config, metric), } } } diff --git a/tools/generate-rust-dashboards/src/sql.rs b/tools/generate-rust-dashboards/src/sql.rs index 3b89e7c379..1135b74523 100644 --- a/tools/generate-rust-dashboards/src/sql.rs +++ b/tools/generate-rust-dashboards/src/sql.rs @@ -94,3 +94,26 @@ impl Query { ); } } + +/// Union query +/// +/// Like `Query`, use this if it helps or use raw SQL if it's easier. +#[derive(Debug, Default)] +pub struct Union { + pub queries: Vec, + pub order_by: Option, +} + +impl Union { + pub fn sql(&self) -> String { + let mut sql = String::default(); + + for (i, q) in self.queries.iter().enumerate() { + if i != 0 { + sql.push_str("UNION ALL\n"); + } + sql.push_str(&format!("{}\n", q.sql())); + } + sql + } +} diff --git a/tools/generate-rust-dashboards/src/team_config.rs b/tools/generate-rust-dashboards/src/team_config.rs index a02056e61d..8fea31c10e 100644 --- a/tools/generate-rust-dashboards/src/team_config.rs +++ b/tools/generate-rust-dashboards/src/team_config.rs @@ -20,19 +20,33 @@ pub fn all_dashboards() -> Vec { ], component_errors: true, sync_metrics: true, - main_dashboard_metrics: vec![DistributionMetric { - kind: DistributionMetricKind::Timing, - display_name: "Places run_maintenance() time", - ping: "metrics", - category: "places_manager", - metric: "run_maintenance_time", - axis_label: "time", - unit: Some(Unit::Milliseconds), - value_divisor: Some(1_000_000), - applications: vec![Android], - link_to: Some("Sync Maintenance Times"), - } - .into()], + main_dashboard_metrics: vec![ + DistributionMetric { + kind: DistributionMetricKind::Timing, + display_name: "Places run_maintenance() time", + ping: "metrics", + category: "places_manager", + metric: "run_maintenance_time", + axis_label: "time", + unit: Some(Unit::Milliseconds), + value_divisor: Some(1_000_000), + applications: vec![Android], + link_to: Some("Sync Maintenance Times"), + } + .into(), + EventsMetric { + display_name: "Logins key regeneration", + ping: "metrics", + category: "logins_store", + metrics: vec![ + "key_regenerated_lost", + "key_regenerated_corrupt", + "key_regenerated_other", + ], + applications: vec![Android], + } + .into(), + ], extra_dashboards: vec![ExtraDashboard { name: "Sync Maintenance Times", metrics: vec![