From 9e9869ee0605d0610d6c94bf6185eb1eabd6b6a2 Mon Sep 17 00:00:00 2001 From: Taddes Date: Tue, 26 Nov 2024 17:04:38 -0600 Subject: [PATCH] feat: glean metrics logic (#1626) feat: glean metrics logic --- Cargo.lock | 16 +- Cargo.toml | 1 + glean/Cargo.toml | 17 ++ glean/metrics.yaml | 40 ++-- glean/pings.yaml | 6 +- glean/src/lib.rs | 3 + glean/src/server_events.rs | 324 +++++++++++++++++++++++++++++++ syncserver-settings/src/lib.rs | 4 + syncserver/Cargo.toml | 1 + syncserver/src/server/mod.rs | 17 ++ syncserver/src/server/test.rs | 9 + syncserver/src/web/extractors.rs | 10 + syncserver/src/web/handlers.rs | 40 ++-- syncstorage-settings/src/lib.rs | 4 + syncstorage-spanner/Cargo.toml | 2 +- 15 files changed, 458 insertions(+), 36 deletions(-) create mode 100644 glean/Cargo.toml create mode 100644 glean/src/lib.rs create mode 100644 glean/src/server_events.rs diff --git a/Cargo.lock b/Cargo.lock index d2257f8462..18ec5f560d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1140,6 +1140,17 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +[[package]] +name = "glean" +version = "0.17.12" +dependencies = [ + "chrono", + "serde 1.0.203", + "serde_derive", + "serde_json", + "uuid", +] + [[package]] name = "glob" version = "0.3.1" @@ -2821,6 +2832,7 @@ dependencies = [ "docopt", "dyn-clone", "futures 0.3.30", + "glean", "hawk", "hex", "hmac", @@ -3431,9 +3443,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.8.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" dependencies = [ "getrandom", "serde 1.0.203", diff --git a/Cargo.toml b/Cargo.toml index d7745cfceb..b675de61ab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -91,6 +91,7 @@ slog-stdlog = "4.1" slog-term = "2.6" tokio = "1" thiserror = "1.0.26" +uuid = { version = "1.11", features = ["serde", "v4"] } [profile.release] # Enables line numbers in Sentry reporting diff --git a/glean/Cargo.toml b/glean/Cargo.toml new file mode 100644 index 0000000000..e077016977 --- /dev/null +++ b/glean/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "glean" +version.workspace = true +license.workspace = true +authors.workspace = true +edition.workspace = true + +[dependencies] +chrono.workspace = true +serde.workspace = true +serde_derive.workspace = true +serde_json.workspace = true +uuid.workspace = true + +[lib] +name = "glean" +path = "src/lib.rs" \ No newline at end of file diff --git a/glean/metrics.yaml b/glean/metrics.yaml index f8f10f10dc..d44c3bf1dc 100644 --- a/glean/metrics.yaml +++ b/glean/metrics.yaml @@ -8,14 +8,12 @@ $schema: moz://mozilla.org/schemas/glean/metrics/2-0-0 # Category syncstorage: - sync_event: + get_collections: type: event description: | Event to record an instance of sync backend activity initiated by client. notification_emails: - - jrconlin@mozilla.com - - pjenvey@mozilla.com - - tkorris@mozilla.com + - sync-backend@mozilla.com bugs: - https://github.com/mozilla-services/syncstorage-rs/issues data_reviews: @@ -23,7 +21,7 @@ syncstorage: expires: never hashed_fxa_uid: - type: uuid + type: string # yamllint disable description: > User identifier. Uses `hashed_fxa_uid` for accurate count of sync actions. @@ -36,10 +34,12 @@ syncstorage: of new keys being generated during resets or timeouts, whenever encryption keys change. # yamllint enable + lifetime: application + send_in_pings: + - sync-dau + - events notification_emails: - - jrconlin@mozilla.com - - pjenvey@mozilla.com - - tkorris@mozilla.com + - sync-backend@mozilla.com bugs: - https://github.com/mozilla-services/syncstorage-rs/issues data_reviews: @@ -53,10 +53,12 @@ syncstorage: Platform from which sync action was initiated. Firefox Desktop, Fenix, or Firefox iOS. # yamllint enable + lifetime: application + send_in_pings: + - sync-dau + - events notification_emails: - - jrconlin@mozilla.com - - pjenvey@mozilla.com - - tkorris@mozilla.com + - sync-backend@mozilla.com bugs: - https://github.com/mozilla-services/syncstorage-rs/issues data_reviews: @@ -70,10 +72,12 @@ syncstorage: Device family from which sync action was initiated. Desktop PC, Tablet, Mobile, and Other. # yamllint enable + lifetime: application + send_in_pings: + - sync-dau + - events notification_emails: - - jrconlin@mozilla.com - - pjenvey@mozilla.com - - tkorris@mozilla.com + - sync-backend@mozilla.com bugs: - https://github.com/mozilla-services/syncstorage-rs/issues data_reviews: @@ -88,10 +92,12 @@ syncstorage: entirely to associate opt-out or removal requests, as they make use of the "deletion-request" ping associated with the client side of Sync. # yamllint enable + lifetime: application + send_in_pings: + - sync-dau + - events notification_emails: - - jrconlin@mozilla.com - - pjenvey@mozilla.com - - tkorris@mozilla.com + - sync-backend@mozilla.com bugs: - https://github.com/mozilla-services/syncstorage-rs/issues data_reviews: diff --git a/glean/pings.yaml b/glean/pings.yaml index 0b86b321af..76d83162a8 100644 --- a/glean/pings.yaml +++ b/glean/pings.yaml @@ -4,14 +4,12 @@ $schema: moz://mozilla.org/schemas/glean/pings/2-0-0 # Name -syncstorage: +sync-dau: # Ping parameters description: | Ping record for sync active use metrics. notification_emails: - - jrconlin@mozilla.com - - pjenvey@mozilla.com - - tkorris@mozilla.com + - sync-backend@mozilla.com bugs: - https://github.com/mozilla-services/syncstorage-rs/issues data_reviews: diff --git a/glean/src/lib.rs b/glean/src/lib.rs new file mode 100644 index 0000000000..b3bb00e21b --- /dev/null +++ b/glean/src/lib.rs @@ -0,0 +1,3 @@ +//! Module related to Glean server metrics. + +pub mod server_events; diff --git a/glean/src/server_events.rs b/glean/src/server_events.rs new file mode 100644 index 0000000000..38582438e1 --- /dev/null +++ b/glean/src/server_events.rs @@ -0,0 +1,324 @@ +//! This Server Events crate encapsulates the core functionality related to +//! emitting Glean server metrics. + +// 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 chrono::Utc; +use serde::{Deserialize, Serialize}; +use serde_json::Value; +use std::collections::HashMap; +use uuid::Uuid; + +/// log type string used to identify logs to process in the Moz Data Pipeline. +const GLEAN_EVENT_MOZLOG_TYPE: &str = "glean-server-event"; + +// Code below is static, regardless of what is defined in `metrics.yaml`: + +/// The GleanEventsLogger produces output in the required format for Glean to ingest. +/// Glean ingestion requires the output to be written to stdout. Writing to a different +/// output will require the consumer to handle any closing as appropriate for the Writer. +pub struct GleanEventsLogger { + /// Application Id to identify application per Glean standards + pub app_id: String, + /// Version of application emitting the event + pub app_display_version: String, + /// Channel to differentiate logs from prod/beta/staging/development + pub app_channel: String, +} + +/// Struct containing request metadata. Record calls can be made with this being left empty. +/// Default impl empty values will be omitted in json from ping struct definition. +#[derive(Default, Serialize, Deserialize)] +pub struct RequestInfo { + pub user_agent: String, + pub ip_address: String, +} + +/// Struct encapsulating client application data to construct Glean ping. +#[derive(Serialize, Deserialize, Debug)] +pub struct ClientInfo { + telemetry_sdk_build: String, + first_run_date: String, + os: String, + os_version: String, + architecture: String, + app_build: String, + app_display_version: String, + app_channel: String, +} + +/// Ping metadata struct. +#[derive(Serialize, Deserialize, Debug)] +pub struct PingInfo { + seq: u32, + start_time: String, + end_time: String, +} + +impl Default for PingInfo { + /// Default impl to create PingInfo. + fn default() -> Self { + // times are ISO-8601 strings, e.g. "2023-12-19T22:09:17.440Z" + let now = Utc::now().to_rfc3339(); + PingInfo { + seq: 0, + start_time: now.clone(), + end_time: now, + } + } +} + +/// Struct containing ping metadata. +#[derive(Serialize, Deserialize, Debug)] +pub struct Ping { + document_namespace: String, + document_type: String, + document_version: String, + document_id: String, + user_agent: Option, + ip_address: Option, + payload: String, +} + +/// Glean Metrics type expressed by a String key of the supported metric types +/// ("string", "quantity", "event", "datetime", "boolean") and a HashMap +/// of each metric (defined in `metrics.yaml`) corresponding to its +/// serialized value. +type Metrics = HashMap>; + +/// Struct defining the `Event` metric type. +#[derive(Debug, Serialize, Deserialize)] +pub struct GleanEvent { + category: String, + name: String, + timestamp: i64, + extra: HashMap, +} + +pub fn new_glean_event(category: &str, name: &str, extra: HashMap) -> GleanEvent { + GleanEvent { + category: category.to_owned(), + name: name.to_owned(), + timestamp: Utc::now().timestamp_millis(), + extra, + } +} + +/// Struct encapsulating the telemetry payload, including the metrics and events, +/// in addition to client and ping metadata. +#[derive(Serialize, Deserialize, Debug)] +struct PingPayload { + client_info: ClientInfo, + ping_info: PingInfo, + metrics: Metrics, + events: Vec, +} + +/// Logging envelope that is serialized for emission to stdout. +#[derive(Serialize, Deserialize)] +struct LogEnvelope { + // MozLog compliant format. https://wiki.mozilla.org/Firefox/Services/Logging + #[serde(rename = "Type")] + log_type: String, + #[serde(rename = "Fields")] + fields: Ping, +} + +impl GleanEventsLogger { + /// Create ClientInfo struct from values defined in GleanEventsLogger. + fn create_client_info(&self) -> ClientInfo { + // Fields with default values are required in the Glean schema, but not used in server context. + ClientInfo { + telemetry_sdk_build: "glean_parser v15.0.2.dev17+g81fec69a".to_owned(), + first_run_date: "Unknown".to_owned(), + os: "Unknown".to_owned(), + os_version: "Unknown".to_owned(), + architecture: "Unknown".to_owned(), + app_build: "Unknown".to_owned(), + app_display_version: self.app_display_version.clone(), + app_channel: self.app_channel.clone(), + } + } + + /// Method used to encapsulate ping metadata and PingPayload. + fn create_ping( + &self, + document_type: &str, + config: &RequestInfo, + payload: &PingPayload, + ) -> Ping { + Ping { + document_namespace: self.app_id.clone(), + document_type: document_type.to_owned(), + document_version: "1".to_owned(), + document_id: Uuid::new_v4().to_string(), + user_agent: Some(config.user_agent.clone()), + ip_address: Some(config.ip_address.clone()), + payload: serde_json::to_string(payload).expect("unable to marshal payload to json."), + } + } + + /// Method called by each ping-specific record method. + /// The goal is to construct the ping, wrap it in the envelope and print to stdout. + fn record( + &self, + document_type: &str, + request_info: &RequestInfo, + metrics: Metrics, + events: Vec, + ) { + let telemetry_payload: PingPayload = PingPayload { + client_info: self.create_client_info(), + ping_info: PingInfo::default(), + metrics, + events, + }; + + let ping: Ping = self.create_ping(document_type, request_info, &telemetry_payload); + + let envelope: LogEnvelope = LogEnvelope { + log_type: GLEAN_EVENT_MOZLOG_TYPE.to_owned(), + fields: ping, + }; + let envelope_json = + serde_json::to_string(&envelope).expect("unable to marshal payload to json."); + println!("{}", envelope_json); + } +} + +// Code below is generated based on the provided `metrics.yaml` file: + +// Metrics of the `event` type. Anything defined in `extra_keys` has it's own struct field. +// The appended `Event` term to any metric of the event type implies the ping event. + +/// Struct containing metadata defined in `extra_keys` if they are defined. Otherwise empty. +pub struct SyncstorageGetCollectionsEvent { + // Metadata for event in `extra_keys`. +} + +// Implementing the EventsPingEvent trait for the generated struct SyncstorageGetCollectionsEvent +impl EventsPingEvent for SyncstorageGetCollectionsEvent { + /// Create a GleanEvent for the above-defined Event struct (SyncstorageGetCollectionsEvent). + /// Any metadata `extra` values are passed into the extra HashMap. + fn glean_event(&self) -> GleanEvent { + // Any `extra_keys` will be output below to be inserted into `extra`. + // If there are none, an empty, immutable HashMap is created. + let extra: HashMap = HashMap::new(); + + new_glean_event("syncstorage", "get_collections", extra) + } +} + +/// Marker trait for events per ping. +pub trait EventsPingEvent { + fn glean_event(&self) -> GleanEvent; +} + +/// Struct containing defined metrics and event(s) from `metrics.yaml`. +/// Encompasses the core Glean Ping Event and its data. +pub struct EventsPing { + pub syncstorage_device_family: String, // Device family from which sync action was initiated. Desktop PC, Tablet, Mobile, and Other. + pub syncstorage_hashed_device_id: String, // Hashed device id that is associated with a given account. + pub syncstorage_hashed_fxa_uid: String, // User identifier. Uses `hashed_fxa_uid` for accurate count of sync actions. + pub syncstorage_platform: String, // Platform from which sync action was initiated. Firefox Desktop, Fenix, or Firefox iOS. + pub event: Option>, // valid event of `EventsPingEvent` for this ping. +} + +impl GleanEventsLogger { + /// General `record_events_ping` function for core Glean Ping Event - Record and submit `events` ping. + /// Collects a HashMap of parametrized key value pairs and events to be recorded. + pub fn record_events_ping(&self, request_info: &RequestInfo, params: &EventsPing) { + // Define the outer `Metrics` map that holds the metric type. + let mut metrics = Metrics::new(); + // Create the inner metric value map to insert into `Metrics`. + let mut string_map: HashMap = HashMap::new(); + // Create corresponding metric value maps to insert into `Metrics`. + string_map.insert( + "syncstorage.device_family".to_owned(), + Value::String(params.syncstorage_device_family.to_string()), + ); + string_map.insert( + "syncstorage.hashed_device_id".to_owned(), + Value::String(params.syncstorage_hashed_device_id.to_string()), + ); + string_map.insert( + "syncstorage.hashed_fxa_uid".to_owned(), + Value::String(params.syncstorage_hashed_fxa_uid.to_string()), + ); + string_map.insert( + "syncstorage.platform".to_owned(), + Value::String(params.syncstorage_platform.to_string()), + ); + metrics.insert("string".to_owned(), string_map); + + let mut events: Vec = Vec::new(); + if let Some(event) = ¶ms.event { + events.push(event.glean_event()); + } + + self.record("events", request_info, metrics, events); + } +} + +impl GleanEventsLogger { + /// Record and submit `events` ping while omitting user request info. + pub fn record_events_ping_without_user_info(&self, params: &EventsPing) { + self.record_events_ping(&RequestInfo::default(), params) + } +} + +/// Core struct defining metric fields for the `sync-dau-ping` (Daily Active Users). +pub struct SyncDauPing { + pub syncstorage_device_family: String, // Device family from which sync action was initiated. Desktop PC, Tablet, Mobile, and Other. + pub syncstorage_hashed_device_id: String, // Hashed device id that is associated with a given account. + pub syncstorage_hashed_fxa_uid: String, // User identifier. Uses `hashed_fxa_uid` for accurate count of sync actions. + pub syncstorage_platform: String, // Platform from which sync action was initiated. Firefox Desktop, Fenix, or Firefox iOS. +} + +impl GleanEventsLogger { + /// General `record_events_ping` - record and submit `sync-dau` ping + pub fn record_sync_dau_ping(&self, request_info: &RequestInfo, params: &SyncDauPing) { + // Define the outer `Metrics` map that holds the metric type. + let mut metrics = Metrics::new(); + // Create the inner metric value map to insert into `Metrics`. + metrics.insert( + "string".to_owned(), + HashMap::from([( + "syncstorage.device_family".to_owned(), + Value::String(params.syncstorage_device_family.to_owned().clone()), + )]), + ); + metrics.insert( + "string".to_owned(), + HashMap::from([( + "syncstorage.hashed_device_id".to_owned(), + Value::String(params.syncstorage_hashed_device_id.to_owned().clone()), + )]), + ); + metrics.insert( + "string".to_owned(), + HashMap::from([( + "syncstorage.hashed_fxa_uid".to_owned(), + Value::String(params.syncstorage_hashed_fxa_uid.to_owned().clone()), + )]), + ); + metrics.insert( + "string".to_owned(), + HashMap::from([( + "syncstorage.platform".to_owned(), + Value::String(params.syncstorage_platform.to_owned().clone()), + )]), + ); + + let events: Vec = Vec::new(); + self.record("sync-dau", request_info, metrics, events); + } +} + +impl GleanEventsLogger { + /// Record and submit `sync-dau` ping while omitting user request info. + pub fn record_sync_dau_ping_without_user_info(&self, params: &SyncDauPing) { + self.record_sync_dau_ping(&RequestInfo::default(), params) + } +} diff --git a/syncserver-settings/src/lib.rs b/syncserver-settings/src/lib.rs index e8bd265b68..6732ffcc0c 100644 --- a/syncserver-settings/src/lib.rs +++ b/syncserver-settings/src/lib.rs @@ -30,6 +30,9 @@ pub struct Settings { pub statsd_host: Option, pub statsd_port: u16, + /// Environment of Sync application (Stage, Prod, Dev, etc). + pub environment: String, + /// Cors Settings pub cors_allowed_origin: Option, pub cors_max_age: Option, @@ -183,6 +186,7 @@ impl Default for Settings { master_secret: Secrets::default(), statsd_host: Some("localhost".to_owned()), statsd_port: 8125, + environment: "dev".to_owned(), human_logs: false, cors_allowed_origin: Some("*".to_owned()), cors_allowed_methods: Some( diff --git a/syncserver/Cargo.toml b/syncserver/Cargo.toml index faf01957aa..a920ee107b 100644 --- a/syncserver/Cargo.toml +++ b/syncserver/Cargo.toml @@ -40,6 +40,7 @@ actix-rt = "2" actix-cors = "0.7" async-trait = "0.1.40" dyn-clone = "1.0.4" +glean = { path = "../glean" } hawk = "5.0" mime = "0.3" # pin to 0.19: https://github.com/getsentry/sentry-rust/issues/277 diff --git a/syncserver/src/server/mod.rs b/syncserver/src/server/mod.rs index 93dfda9b05..1539ebcc71 100644 --- a/syncserver/src/server/mod.rs +++ b/syncserver/src/server/mod.rs @@ -24,6 +24,8 @@ use syncstorage_settings::{Deadman, ServerLimits}; use tokio::{sync::RwLock, time}; use crate::error::ApiError; +use glean::server_events::GleanEventsLogger; + use crate::tokenserver; use crate::web::{handlers, middleware}; @@ -57,6 +59,11 @@ pub struct ServerState { pub quota_enabled: bool, pub deadman: Arc>, + + /// Glean metrics logger. + pub glean_logger: Arc, + + pub glean_enabled: bool, } pub fn cfg_path(path: &str) -> String { @@ -266,6 +273,14 @@ impl Server { &Metrics::from(&metrics), blocking_threadpool.clone(), )?; + let glean_logger = Arc::new(GleanEventsLogger { + // app_id corresponds to probe-scraper entry. + // https://github.com/mozilla/probe-scraper/blob/main/repositories.yaml + app_id: "syncstorage".to_owned(), + app_display_version: env!("CARGO_PKG_VERSION").to_owned(), + app_channel: settings.environment.clone(), + }); + let glean_enabled = settings.syncstorage.glean_enabled; let worker_thread_count = calculate_worker_max_blocking_threads(settings.worker_max_blocking_threads); let limits = Arc::new(settings.syncstorage.limits); @@ -309,6 +324,8 @@ impl Server { port, quota_enabled, deadman: Arc::clone(&deadman), + glean_logger: Arc::clone(&glean_logger), + glean_enabled, }; build_app!( diff --git a/syncserver/src/server/test.rs b/syncserver/src/server/test.rs index e5d7f46d39..869052666c 100644 --- a/syncserver/src/server/test.rs +++ b/syncserver/src/server/test.rs @@ -85,6 +85,13 @@ async fn get_test_state(settings: &Settings) -> ServerState { let blocking_threadpool = Arc::new(BlockingThreadpool::new( settings.worker_max_blocking_threads, )); + let glean_logger = Arc::new(GleanEventsLogger { + // app_id corresponds to probe-scraper entry. + // https://github.com/mozilla/probe-scraper/blob/main/repositories.yaml + app_id: "syncstorage".to_owned(), + app_display_version: env!("CARGO_PKG_VERSION").to_owned(), + app_channel: settings.environment.clone(), + }); ServerState { db_pool: Box::new( @@ -101,6 +108,8 @@ async fn get_test_state(settings: &Settings) -> ServerState { port: settings.port, quota_enabled: settings.syncstorage.enable_quota, deadman: Arc::new(RwLock::new(Deadman::from(&settings.syncstorage))), + glean_logger, + glean_enabled: settings.syncstorage.glean_enabled, } } diff --git a/syncserver/src/web/extractors.rs b/syncserver/src/web/extractors.rs index 56b7961062..4bd617b083 100644 --- a/syncserver/src/web/extractors.rs +++ b/syncserver/src/web/extractors.rs @@ -1750,6 +1750,7 @@ mod tests { use tokio::sync::RwLock; use crate::server::ServerState; + use glean::server_events::GleanEventsLogger; use syncstorage_db::mock::{MockDb, MockDbPool}; use crate::web::auth::HawkPayload; @@ -1775,6 +1776,13 @@ mod tests { fn make_state() -> ServerState { let syncserver_settings = GlobalSettings::default(); let syncstorage_settings = SyncstorageSettings::default(); + let glean_logger = Arc::new(GleanEventsLogger { + // app_id corresponds to probe-scraper entry. + // https://github.com/mozilla/probe-scraper/blob/main/repositories.yaml + app_id: "syncstorage".to_owned(), + app_display_version: env!("CARGO_PKG_VERSION").to_owned(), + app_channel: "prod".to_owned(), + }); ServerState { db_pool: Box::new(MockDbPool::new()), limits: Arc::clone(&SERVER_LIMITS), @@ -1788,6 +1796,8 @@ mod tests { .unwrap(), quota_enabled: syncstorage_settings.enable_quota, deadman: Arc::new(RwLock::new(Deadman::default())), + glean_logger, + glean_enabled: syncstorage_settings.glean_enabled, } } diff --git a/syncserver/src/web/handlers.rs b/syncserver/src/web/handlers.rs index 4101755ce3..4af2259f8d 100644 --- a/syncserver/src/web/handlers.rs +++ b/syncserver/src/web/handlers.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use std::convert::Into; use std::time::{Duration, Instant}; -use crate::server::user_agent::get_device_info; +use crate::server::user_agent::{get_device_info, DeviceInfo}; use actix_web::{ http::{header, StatusCode}, web::Data, @@ -30,24 +30,40 @@ use crate::{ }, }; +use glean::server_events::{EventsPing, RequestInfo, SyncstorageGetCollectionsEvent}; + pub const ONE_KB: f64 = 1024.0; pub async fn get_collections( meta: MetaRequest, db_pool: DbTransactionPool, request: HttpRequest, + state: Data, ) -> Result { - // The values below, prefixed by `_`, are temporarily and intentionally ignored at present. - // They will be passed to the Glean logic we will implement to emit metrics. - // We'd like for the data to be ready and in place to pass to that logic. - let _hashed_fxa_uid: String = meta.user_id.hashed_fxa_uid.clone(); - let _hashed_device_id: String = meta.user_id.hashed_device_id.clone(); - let user_agent = request - .headers() - .get(header::USER_AGENT) - .and_then(|header| header.to_str().ok()) - .unwrap_or("none"); - let _device_info = get_device_info(user_agent); + if state.glean_enabled { + // Values below are be passed to the Glean logic to emit metrics. + // This is used to measure DAU (Daily Active Use) of Sync. + let user_agent = request + .headers() + .get(header::USER_AGENT) + .and_then(|header| header.to_str().ok()) + .unwrap_or(""); + let device_info: DeviceInfo = get_device_info(user_agent); + + state.glean_logger.record_events_ping( + &RequestInfo { + user_agent: user_agent.to_owned(), + ip_address: "".to_owned(), + }, + &EventsPing { + syncstorage_device_family: device_info.device_family.to_string(), + syncstorage_hashed_device_id: meta.user_id.hashed_device_id.clone(), + syncstorage_hashed_fxa_uid: meta.user_id.hashed_fxa_uid.clone(), + syncstorage_platform: device_info.platform.to_string(), + event: Some(Box::new(SyncstorageGetCollectionsEvent {})), + }, + ); + } db_pool .transaction_http(request, |db| async move { diff --git a/syncstorage-settings/src/lib.rs b/syncstorage-settings/src/lib.rs index 1d3b43a54c..0bf7769033 100644 --- a/syncstorage-settings/src/lib.rs +++ b/syncstorage-settings/src/lib.rs @@ -93,6 +93,9 @@ pub struct Settings { pub enable_quota: bool, pub enforce_quota: bool, + /// Whether Glean telemetry metric emission is enabled. + pub glean_enabled: bool, + pub spanner_emulator_host: Option, pub enabled: bool, @@ -122,6 +125,7 @@ impl Default for Settings { statsd_label: "syncstorage".to_string(), enable_quota: false, enforce_quota: false, + glean_enabled: true, spanner_emulator_host: None, enabled: true, lbheartbeat_ttl: None, diff --git a/syncstorage-spanner/Cargo.toml b/syncstorage-spanner/Cargo.toml index 6bde306438..c2eae5f3a1 100644 --- a/syncstorage-spanner/Cargo.toml +++ b/syncstorage-spanner/Cargo.toml @@ -14,6 +14,7 @@ futures.workspace = true http.workspace = true slog-scope.workspace = true thiserror.workspace = true +uuid.workspace = true async-trait = "0.1.40" google-cloud-rust-raw = { version = "0.16.1", features = ["spanner"] } @@ -36,7 +37,6 @@ tokio = { workspace = true, features = [ "sync", ] } url = "2.1" -uuid = { version = "1.6", features = ["serde", "v4"] } [[bin]] name = "purge_ttl"