From 178b20efed866ae43495d76c1b308647862303ee Mon Sep 17 00:00:00 2001 From: Ben Dean-Kawamura Date: Tue, 10 Feb 2026 15:54:17 -0500 Subject: [PATCH] Bug 2015832 - Group rust component errors by build id --- .../src/metrics/rust_component_errors.rs | 71 +++++++++++++++++-- tools/generate-rust-dashboards/src/schema.rs | 30 ++++++++ tools/generate-rust-dashboards/src/sql.rs | 8 +-- 3 files changed, 99 insertions(+), 10 deletions(-) diff --git a/tools/generate-rust-dashboards/src/metrics/rust_component_errors.rs b/tools/generate-rust-dashboards/src/metrics/rust_component_errors.rs index c2d8cce646..915c4812cc 100644 --- a/tools/generate-rust-dashboards/src/metrics/rust_component_errors.rs +++ b/tools/generate-rust-dashboards/src/metrics/rust_component_errors.rs @@ -9,7 +9,7 @@ use crate::{ schema::{ Dashboard, DashboardBuilder, DataLink, Datasource, FieldConfig, FieldConfigCustom, FieldConfigDefaults, GridPos, LogPanel, Panel, QueryVariable, QueryVariableQuery, Target, - TextBoxVariable, TimeSeriesPanel, Transformation, + TextBoxVariable, TimeSeriesPanel, Transformation, VariableSortOrder, }, sql::Query, util::UrlBuilder, @@ -100,12 +100,14 @@ pub fn extra_dashboard(config: &TeamConfig) -> Result { ); builder.add_application_variable(config)?; builder.add_channel_variable(); + builder.add_variable(error_type_variable()); + builder.add_variable(version_variable()); + builder.add_variable(build_date_variable()); builder.add_variable(TextBoxVariable { label: "Search details".into(), name: "details".into(), ..TextBoxVariable::default() }); - builder.add_variable(error_type_variable()); builder.add_filter_sql_variable(); builder.add_panel_full(error_list_count_panel()); @@ -134,19 +136,72 @@ ORDER BY metrics.string.rust_component_errors_error_type", } } +pub fn version_variable() -> QueryVariable { + let query = QueryVariableQuery::from_sql( + "\ +SELECT 'All' as text, '' as value +UNION ALL +SELECT version as text, version as value +FROM ( + SELECT DISTINCT CAST(mozfun.norm.extract_version(client_info.app_display_version, 'major') AS STRING) as version + FROM mozdata.fenix.rust_component_errors + WHERE submission_timestamp > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 14 day) + AND mozfun.norm.extract_version(client_info.app_display_version, 'major') IS NOT NULL + ORDER BY 1 DESC +)", + ); + + QueryVariable { + label: "Version".into(), + name: "version".into(), + datasource: Datasource::bigquery(), + query, + sort: Some(VariableSortOrder::AlphabeticalDescending), + ..QueryVariable::default() + } +} + +pub fn build_date_variable() -> QueryVariable { + let query = QueryVariableQuery::from_sql( + "\ +SELECT 'None' as text, '' as value +UNION ALL +SELECT build_date as text, build_date as value +FROM ( + SELECT DISTINCT SUBSTR(client_info.build_date, 0, 10) as build_date + FROM mozdata.fenix.rust_component_errors + WHERE submission_timestamp > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 14 day) + ORDER BY 1 DESC +)", + ); + + QueryVariable { + label: "Group by build date".into(), + name: "build_date".into(), + datasource: Datasource::bigquery(), + sort: Some(VariableSortOrder::AlphabeticalDescending), + query, + ..QueryVariable::default() + } +} + fn error_list_count_panel() -> Panel { let mut query = Query { - select: vec!["$__timeGroup(submission_timestamp, $__interval) as time".into()], + select: vec![ + "$__timeGroup(submission_timestamp, $__interval) as time".into(), + "IF('${build_date}' = '', '', IF(build_date < '${build_date}', '< ${build_date}', '>= ${build_date}')) as build_date".into(), + ], where_: vec![ "error_type='${error_type}'".into(), "$__timeFilter(submission_timestamp)".into(), "normalized_channel = '${channel}'".into(), - "('${details}' = '' OR details LIKE '%${details}%')".into(), + "'${version}' = '' OR version = CAST('${version}' AS NUMERIC)".into(), + "'${details}' = '' OR details LIKE '%${details}%'".into(), "${filter_sql}".into(), ], from: error_subquery().as_subquery(), - group_by: Some("1".into()), - order_by: Some("time DESC".into()), + group_by: Some("1, 2".into()), + order_by: Some("1 ASC, 2 ASC".into()), ..Query::default() }; query.add_count_per_day_column("COUNT(*)", "errors"); @@ -183,6 +238,7 @@ fn error_list_log_panel() -> Panel { "error_type='${error_type}'".into(), "$__timeFilter(submission_timestamp)".into(), "normalized_channel = '${channel}'".into(), + "'${version}' = '' OR version = CAST('${version}' AS NUMERIC)".into(), "('${details}' = '' OR details LIKE '%${details}%')".into(), "${filter_sql}".into(), ], @@ -210,6 +266,9 @@ fn error_list_log_panel() -> Panel { fn error_subquery() -> Query { let mut subquery = Query { select: vec![ + "SUBSTR(client_info.build_date, 0, 10) as build_date".into(), + "mozfun.norm.extract_version(client_info.app_display_version, 'major') as version" + .into(), "metrics.string.rust_component_errors_error_type as error_type".into(), "metrics.string.rust_component_errors_details as details".into(), "metrics.string_list.rust_component_errors_breadcrumbs as breadcrumbs".into(), diff --git a/tools/generate-rust-dashboards/src/schema.rs b/tools/generate-rust-dashboards/src/schema.rs index 845a8eb838..f71751e162 100644 --- a/tools/generate-rust-dashboards/src/schema.rs +++ b/tools/generate-rust-dashboards/src/schema.rs @@ -265,6 +265,7 @@ pub struct QueryVariable { pub multi: bool, pub allow_custom_value: bool, pub query: QueryVariableQuery, + pub sort: Option, pub hide: VariableHide, } @@ -326,6 +327,17 @@ pub enum CustomVariableSelection { Multiple { value: Vec }, } +pub enum VariableSortOrder { + AlphabeticalAscending, + AlphabeticalDescending, + NumericalAscending, + NumericalDescending, + AlphabeticalCaseInsensitiveAscending, + AlphabeticalCaseInsensitiveDescending, + NaturalAscending, + NaturalDescending, +} + impl Default for Dashboard { fn default() -> Self { Self { @@ -524,6 +536,24 @@ impl Serialize for SortOrder { } } +impl Serialize for VariableSortOrder { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + Self::AlphabeticalAscending => serializer.serialize_u32(1), + Self::AlphabeticalDescending => serializer.serialize_u32(2), + Self::NumericalAscending => serializer.serialize_u32(3), + Self::NumericalDescending => serializer.serialize_u32(4), + Self::AlphabeticalCaseInsensitiveAscending => serializer.serialize_u32(5), + Self::AlphabeticalCaseInsensitiveDescending => serializer.serialize_u32(6), + Self::NaturalAscending => serializer.serialize_u32(7), + Self::NaturalDescending => serializer.serialize_u32(8), + } + } +} + impl GridPos { /// Create a GridPos from a height only /// diff --git a/tools/generate-rust-dashboards/src/sql.rs b/tools/generate-rust-dashboards/src/sql.rs index 0b51d4a728..2f1d11eb21 100644 --- a/tools/generate-rust-dashboards/src/sql.rs +++ b/tools/generate-rust-dashboards/src/sql.rs @@ -98,12 +98,12 @@ impl Query { // TODO: Add UNIONs once we are enable the glean pipeline for iOS and/or Desktop // let from = format!(" // ( - // SELECT * FROM mozdata.fenix.{table_name} WHERE '${{application}}' = 'android' - // UNION ALL SELECT * FROM mozdata.firefox_ios.{table_name} WHERE '${{application}}' = 'ios' - // UNION ALL SELECT * FROM mozdata.firefox_desktop.{table_name} WHERE '${{application}}' = 'desktop' + // SELECT * FROM mozdata.fenix.{table_name} WHERE '${{application}}' = 'firefox_android' + // UNION ALL SELECT * FROM mozdata.firefox_ios.{table_name} WHERE '${{application}}' = 'firefox_ios' + // UNION ALL SELECT * FROM mozdata.firefox_desktop.{table_name} WHERE '${{application}}' = 'firefox_desktop' // )"); self.from = format!( - "(SELECT * FROM mozdata.fenix.{table_name} WHERE '${{application}}' = 'android')" + "(SELECT * FROM mozdata.fenix.{table_name} WHERE '${{application}}' = 'firefox_android')" ); } }