Add app events

Joseph T. Lyons created

Change summary

crates/client/src/telemetry.rs  | 38 +++++++++++++++++++++++-----------
crates/client2/src/telemetry.rs | 38 +++++++++++++++++++++++-----------
crates/zed/src/main.rs          | 23 +++++++++++++++++---
crates/zed2/src/main.rs         | 20 ++++++++++++++---
4 files changed, 87 insertions(+), 32 deletions(-)

Detailed changes

crates/client/src/telemetry.rs 🔗

@@ -109,6 +109,10 @@ pub enum ClickhouseEvent {
         virtual_memory_in_bytes: u64,
         milliseconds_since_first_event: i64,
     },
+    App {
+        operation: &'static str,
+        milliseconds_since_first_event: i64,
+    },
 }
 
 #[cfg(debug_assertions)]
@@ -168,13 +172,8 @@ impl Telemetry {
         let mut state = self.state.lock();
         state.installation_id = installation_id.map(|id| id.into());
         state.session_id = Some(session_id.into());
-        let has_clickhouse_events = !state.clickhouse_events_queue.is_empty();
         drop(state);
 
-        if has_clickhouse_events {
-            self.flush_clickhouse_events();
-        }
-
         let this = self.clone();
         cx.spawn(|mut cx| async move {
             // Avoiding calling `System::new_all()`, as there have been crashes related to it
@@ -256,7 +255,7 @@ impl Telemetry {
             milliseconds_since_first_event: self.milliseconds_since_first_event(),
         };
 
-        self.report_clickhouse_event(event, telemetry_settings)
+        self.report_clickhouse_event(event, telemetry_settings, false)
     }
 
     pub fn report_copilot_event(
@@ -273,7 +272,7 @@ impl Telemetry {
             milliseconds_since_first_event: self.milliseconds_since_first_event(),
         };
 
-        self.report_clickhouse_event(event, telemetry_settings)
+        self.report_clickhouse_event(event, telemetry_settings, false)
     }
 
     pub fn report_assistant_event(
@@ -290,7 +289,7 @@ impl Telemetry {
             milliseconds_since_first_event: self.milliseconds_since_first_event(),
         };
 
-        self.report_clickhouse_event(event, telemetry_settings)
+        self.report_clickhouse_event(event, telemetry_settings, false)
     }
 
     pub fn report_call_event(
@@ -307,7 +306,7 @@ impl Telemetry {
             milliseconds_since_first_event: self.milliseconds_since_first_event(),
         };
 
-        self.report_clickhouse_event(event, telemetry_settings)
+        self.report_clickhouse_event(event, telemetry_settings, false)
     }
 
     pub fn report_cpu_event(
@@ -322,7 +321,7 @@ impl Telemetry {
             milliseconds_since_first_event: self.milliseconds_since_first_event(),
         };
 
-        self.report_clickhouse_event(event, telemetry_settings)
+        self.report_clickhouse_event(event, telemetry_settings, false)
     }
 
     pub fn report_memory_event(
@@ -337,7 +336,21 @@ impl Telemetry {
             milliseconds_since_first_event: self.milliseconds_since_first_event(),
         };
 
-        self.report_clickhouse_event(event, telemetry_settings)
+        self.report_clickhouse_event(event, telemetry_settings, false)
+    }
+
+    // app_events are called at app open and app close, so flush is set to immediately send
+    pub fn report_app_event(
+        self: &Arc<Self>,
+        telemetry_settings: TelemetrySettings,
+        operation: &'static str,
+    ) {
+        let event = ClickhouseEvent::App {
+            operation,
+            milliseconds_since_first_event: self.milliseconds_since_first_event(),
+        };
+
+        self.report_clickhouse_event(event, telemetry_settings, true)
     }
 
     fn milliseconds_since_first_event(&self) -> i64 {
@@ -358,6 +371,7 @@ impl Telemetry {
         self: &Arc<Self>,
         event: ClickhouseEvent,
         telemetry_settings: TelemetrySettings,
+        immediate_flush: bool,
     ) {
         if !telemetry_settings.metrics {
             return;
@@ -370,7 +384,7 @@ impl Telemetry {
             .push(ClickhouseEventWrapper { signed_in, event });
 
         if state.installation_id.is_some() {
-            if state.clickhouse_events_queue.len() >= MAX_QUEUE_LEN {
+            if immediate_flush || state.clickhouse_events_queue.len() >= MAX_QUEUE_LEN {
                 drop(state);
                 self.flush_clickhouse_events();
             } else {

crates/client2/src/telemetry.rs 🔗

@@ -107,6 +107,10 @@ pub enum ClickhouseEvent {
         virtual_memory_in_bytes: u64,
         milliseconds_since_first_event: i64,
     },
+    App {
+        operation: &'static str,
+        milliseconds_since_first_event: i64,
+    },
 }
 
 #[cfg(debug_assertions)]
@@ -163,13 +167,8 @@ impl Telemetry {
         let mut state = self.state.lock();
         state.installation_id = installation_id.map(|id| id.into());
         state.session_id = Some(session_id.into());
-        let has_clickhouse_events = !state.clickhouse_events_queue.is_empty();
         drop(state);
 
-        if has_clickhouse_events {
-            self.flush_clickhouse_events();
-        }
-
         let this = self.clone();
         cx.spawn(|cx| async move {
             // Avoiding calling `System::new_all()`, as there have been crashes related to it
@@ -257,7 +256,7 @@ impl Telemetry {
             milliseconds_since_first_event: self.milliseconds_since_first_event(),
         };
 
-        self.report_clickhouse_event(event, telemetry_settings)
+        self.report_clickhouse_event(event, telemetry_settings, false)
     }
 
     pub fn report_copilot_event(
@@ -274,7 +273,7 @@ impl Telemetry {
             milliseconds_since_first_event: self.milliseconds_since_first_event(),
         };
 
-        self.report_clickhouse_event(event, telemetry_settings)
+        self.report_clickhouse_event(event, telemetry_settings, false)
     }
 
     pub fn report_assistant_event(
@@ -291,7 +290,7 @@ impl Telemetry {
             milliseconds_since_first_event: self.milliseconds_since_first_event(),
         };
 
-        self.report_clickhouse_event(event, telemetry_settings)
+        self.report_clickhouse_event(event, telemetry_settings, false)
     }
 
     pub fn report_call_event(
@@ -308,7 +307,7 @@ impl Telemetry {
             milliseconds_since_first_event: self.milliseconds_since_first_event(),
         };
 
-        self.report_clickhouse_event(event, telemetry_settings)
+        self.report_clickhouse_event(event, telemetry_settings, false)
     }
 
     pub fn report_cpu_event(
@@ -323,7 +322,7 @@ impl Telemetry {
             milliseconds_since_first_event: self.milliseconds_since_first_event(),
         };
 
-        self.report_clickhouse_event(event, telemetry_settings)
+        self.report_clickhouse_event(event, telemetry_settings, false)
     }
 
     pub fn report_memory_event(
@@ -338,7 +337,21 @@ impl Telemetry {
             milliseconds_since_first_event: self.milliseconds_since_first_event(),
         };
 
-        self.report_clickhouse_event(event, telemetry_settings)
+        self.report_clickhouse_event(event, telemetry_settings, false)
+    }
+
+    // app_events are called at app open and app close, so flush is set to immediately send
+    pub fn report_app_event(
+        self: &Arc<Self>,
+        telemetry_settings: TelemetrySettings,
+        operation: &'static str,
+    ) {
+        let event = ClickhouseEvent::App {
+            operation,
+            milliseconds_since_first_event: self.milliseconds_since_first_event(),
+        };
+
+        self.report_clickhouse_event(event, telemetry_settings, true)
     }
 
     fn milliseconds_since_first_event(&self) -> i64 {
@@ -359,6 +372,7 @@ impl Telemetry {
         self: &Arc<Self>,
         event: ClickhouseEvent,
         telemetry_settings: TelemetrySettings,
+        immediate_flush: bool,
     ) {
         if !telemetry_settings.metrics {
             return;
@@ -371,7 +385,7 @@ impl Telemetry {
             .push(ClickhouseEventWrapper { signed_in, event });
 
         if state.installation_id.is_some() {
-            if state.clickhouse_events_queue.len() >= MAX_QUEUE_LEN {
+            if immediate_flush || state.clickhouse_events_queue.len() >= MAX_QUEUE_LEN {
                 drop(state);
                 self.flush_clickhouse_events();
             } else {

crates/zed/src/main.rs 🔗

@@ -65,7 +65,8 @@ fn main() {
     log::info!("========== starting zed ==========");
     let mut app = gpui::App::new(Assets).unwrap();
 
-    let installation_id = app.background().block(installation_id()).ok();
+    let (installation_id, existing_installation_id_found) =
+        app.background().block(installation_id()).ok().unzip();
     let session_id = Uuid::new_v4().to_string();
     init_panic_hook(&app, installation_id.clone(), session_id.clone());
 
@@ -166,6 +167,20 @@ fn main() {
         .detach();
 
         client.telemetry().start(installation_id, session_id, cx);
+        // TODO:
+        // Cleanly identify open / first open
+        // What should we do if we fail when looking for installation_id?
+        // - set to true, false, or skip?
+        // Report closed
+        // Copy logic to zed2
+        let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
+        let event_operation = match existing_installation_id_found {
+            Some(true) => "open",
+            _ => "first open",
+        };
+        client
+            .telemetry()
+            .report_app_event(telemetry_settings, event_operation);
 
         let app_state = Arc::new(AppState {
             languages,
@@ -317,11 +332,11 @@ async fn authenticate(client: Arc<Client>, cx: &AsyncAppContext) -> Result<()> {
     Ok::<_, anyhow::Error>(())
 }
 
-async fn installation_id() -> Result<String> {
+async fn installation_id() -> Result<(String, bool)> {
     let legacy_key_name = "device_id";
 
     if let Ok(Some(installation_id)) = KEY_VALUE_STORE.read_kvp(legacy_key_name) {
-        Ok(installation_id)
+        Ok((installation_id, true))
     } else {
         let installation_id = Uuid::new_v4().to_string();
 
@@ -329,7 +344,7 @@ async fn installation_id() -> Result<String> {
             .write_kvp(legacy_key_name.to_string(), installation_id.clone())
             .await?;
 
-        Ok(installation_id)
+        Ok((installation_id, false))
     }
 }
 

crates/zed2/src/main.rs 🔗

@@ -71,7 +71,11 @@ fn main() {
     log::info!("========== starting zed ==========");
     let app = App::production(Arc::new(Assets));
 
-    let installation_id = app.background_executor().block(installation_id()).ok();
+    let (installation_id, existing_installation_id_found) = app
+        .background_executor()
+        .block(installation_id())
+        .ok()
+        .unzip();
     let session_id = Uuid::new_v4().to_string();
     init_panic_hook(&app, installation_id.clone(), session_id.clone());
 
@@ -173,6 +177,14 @@ fn main() {
         // .detach();
 
         client.telemetry().start(installation_id, session_id, cx);
+        let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
+        let event_operation = match existing_installation_id_found {
+            Some(true) => "open",
+            _ => "first open",
+        };
+        client
+            .telemetry()
+            .report_app_event(telemetry_settings, event_operation);
 
         let app_state = Arc::new(AppState {
             languages,
@@ -333,11 +345,11 @@ fn main() {
 //     Ok::<_, anyhow::Error>(())
 // }
 
-async fn installation_id() -> Result<String> {
+async fn installation_id() -> Result<(String, bool)> {
     let legacy_key_name = "device_id";
 
     if let Ok(Some(installation_id)) = KEY_VALUE_STORE.read_kvp(legacy_key_name) {
-        Ok(installation_id)
+        Ok((installation_id, true))
     } else {
         let installation_id = Uuid::new_v4().to_string();
 
@@ -345,7 +357,7 @@ async fn installation_id() -> Result<String> {
             .write_kvp(legacy_key_name.to_string(), installation_id.clone())
             .await?;
 
-        Ok(installation_id)
+        Ok((installation_id, false))
     }
 }