telemetry: Reduce the amount of telemetry events fired (#36060)

1. Extension loaded events are now condensed into a single event with a
Vec of (extension_id, extension_version) called id_and_versions.
2. Editor Saved & AutoSaved are merged into a singular event with a type
field that is either "manual" or "autosave”.
3. Editor Edited event will only fire once every 10 minutes now.
4. Editor Closed event is fired when an editor item (tab) is removed
from a pane



cc: @katie-z-geer 

Release Notes:

- N/A

---------

Co-authored-by: Marshall Bowers <git@maxdeviant.com>
This commit is contained in:
Anthony Eid 2025-08-12 15:56:27 -04:00 committed by GitHub
parent 628b1058be
commit 255bb0a3f8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 110 additions and 43 deletions

View file

@ -340,22 +340,35 @@ impl Telemetry {
} }
pub fn log_edit_event(self: &Arc<Self>, environment: &'static str, is_via_ssh: bool) { pub fn log_edit_event(self: &Arc<Self>, environment: &'static str, is_via_ssh: bool) {
static LAST_EVENT_TIME: Mutex<Option<Instant>> = Mutex::new(None);
let mut state = self.state.lock(); let mut state = self.state.lock();
let period_data = state.event_coalescer.log_event(environment); let period_data = state.event_coalescer.log_event(environment);
drop(state); drop(state);
if let Some((start, end, environment)) = period_data { if let Some(mut last_event) = LAST_EVENT_TIME.try_lock() {
let duration = end let current_time = std::time::Instant::now();
.saturating_duration_since(start) let last_time = last_event.get_or_insert(current_time);
.min(Duration::from_secs(60 * 60 * 24))
.as_millis() as i64;
telemetry::event!( if current_time.duration_since(*last_time) > Duration::from_secs(60 * 10) {
"Editor Edited", *last_time = current_time;
duration = duration, } else {
environment = environment, return;
is_via_ssh = is_via_ssh }
);
if let Some((start, end, environment)) = period_data {
let duration = end
.saturating_duration_since(start)
.min(Duration::from_secs(60 * 60 * 24))
.as_millis() as i64;
telemetry::event!(
"Editor Edited",
duration = duration,
environment = environment,
is_via_ssh = is_via_ssh
);
}
} }
} }

View file

@ -250,6 +250,24 @@ pub type RenderDiffHunkControlsFn = Arc<
) -> AnyElement, ) -> AnyElement,
>; >;
enum ReportEditorEvent {
Saved { auto_saved: bool },
EditorOpened,
ZetaTosClicked,
Closed,
}
impl ReportEditorEvent {
pub fn event_type(&self) -> &'static str {
match self {
Self::Saved { .. } => "Editor Saved",
Self::EditorOpened => "Editor Opened",
Self::ZetaTosClicked => "Edit Prediction Provider ToS Clicked",
Self::Closed => "Editor Closed",
}
}
}
struct InlineValueCache { struct InlineValueCache {
enabled: bool, enabled: bool,
inlays: Vec<InlayId>, inlays: Vec<InlayId>,
@ -2325,7 +2343,7 @@ impl Editor {
} }
if editor.mode.is_full() { if editor.mode.is_full() {
editor.report_editor_event("Editor Opened", None, cx); editor.report_editor_event(ReportEditorEvent::EditorOpened, None, cx);
} }
editor editor
@ -9124,7 +9142,7 @@ impl Editor {
.on_mouse_down(MouseButton::Left, |_, window, _| window.prevent_default()) .on_mouse_down(MouseButton::Left, |_, window, _| window.prevent_default())
.on_click(cx.listener(|this, _event, window, cx| { .on_click(cx.listener(|this, _event, window, cx| {
cx.stop_propagation(); cx.stop_propagation();
this.report_editor_event("Edit Prediction Provider ToS Clicked", None, cx); this.report_editor_event(ReportEditorEvent::ZetaTosClicked, None, cx);
window.dispatch_action( window.dispatch_action(
zed_actions::OpenZedPredictOnboarding.boxed_clone(), zed_actions::OpenZedPredictOnboarding.boxed_clone(),
cx, cx,
@ -20547,7 +20565,7 @@ impl Editor {
fn report_editor_event( fn report_editor_event(
&self, &self,
event_type: &'static str, reported_event: ReportEditorEvent,
file_extension: Option<String>, file_extension: Option<String>,
cx: &App, cx: &App,
) { ) {
@ -20581,15 +20599,30 @@ impl Editor {
.show_edit_predictions; .show_edit_predictions;
let project = project.read(cx); let project = project.read(cx);
telemetry::event!( let event_type = reported_event.event_type();
event_type,
file_extension, if let ReportEditorEvent::Saved { auto_saved } = reported_event {
vim_mode, telemetry::event!(
copilot_enabled, event_type,
copilot_enabled_for_language, type = if auto_saved {"autosave"} else {"manual"},
edit_predictions_provider, file_extension,
is_via_ssh = project.is_via_ssh(), vim_mode,
); copilot_enabled,
copilot_enabled_for_language,
edit_predictions_provider,
is_via_ssh = project.is_via_ssh(),
);
} else {
telemetry::event!(
event_type,
file_extension,
vim_mode,
copilot_enabled,
copilot_enabled_for_language,
edit_predictions_provider,
is_via_ssh = project.is_via_ssh(),
);
};
} }
/// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines, /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,

View file

@ -1,7 +1,7 @@
use crate::{ use crate::{
Anchor, Autoscroll, Editor, EditorEvent, EditorSettings, ExcerptId, ExcerptRange, FormatTarget, Anchor, Autoscroll, Editor, EditorEvent, EditorSettings, ExcerptId, ExcerptRange, FormatTarget,
MultiBuffer, MultiBufferSnapshot, NavigationData, SearchWithinRange, SelectionEffects, MultiBuffer, MultiBufferSnapshot, NavigationData, ReportEditorEvent, SearchWithinRange,
ToPoint as _, SelectionEffects, ToPoint as _,
display_map::HighlightKey, display_map::HighlightKey,
editor_settings::SeedQuerySetting, editor_settings::SeedQuerySetting,
persistence::{DB, SerializedEditor}, persistence::{DB, SerializedEditor},
@ -776,6 +776,10 @@ impl Item for Editor {
} }
} }
fn on_removed(&self, cx: &App) {
self.report_editor_event(ReportEditorEvent::Closed, None, cx);
}
fn deactivated(&mut self, _: &mut Window, cx: &mut Context<Self>) { fn deactivated(&mut self, _: &mut Window, cx: &mut Context<Self>) {
let selection = self.selections.newest_anchor(); let selection = self.selections.newest_anchor();
self.push_to_nav_history(selection.head(), None, true, false, cx); self.push_to_nav_history(selection.head(), None, true, false, cx);
@ -815,9 +819,9 @@ impl Item for Editor {
) -> Task<Result<()>> { ) -> Task<Result<()>> {
// Add meta data tracking # of auto saves // Add meta data tracking # of auto saves
if options.autosave { if options.autosave {
self.report_editor_event("Editor Autosaved", None, cx); self.report_editor_event(ReportEditorEvent::Saved { auto_saved: true }, None, cx);
} else { } else {
self.report_editor_event("Editor Saved", None, cx); self.report_editor_event(ReportEditorEvent::Saved { auto_saved: false }, None, cx);
} }
let buffers = self.buffer().clone().read(cx).all_buffers(); let buffers = self.buffer().clone().read(cx).all_buffers();
@ -896,7 +900,11 @@ impl Item for Editor {
.path .path
.extension() .extension()
.map(|a| a.to_string_lossy().to_string()); .map(|a| a.to_string_lossy().to_string());
self.report_editor_event("Editor Saved", file_extension, cx); self.report_editor_event(
ReportEditorEvent::Saved { auto_saved: false },
file_extension,
cx,
);
project.update(cx, |project, cx| project.save_buffer_as(buffer, path, cx)) project.update(cx, |project, cx| project.save_buffer_as(buffer, path, cx))
} }
@ -997,12 +1005,16 @@ impl Item for Editor {
) { ) {
self.workspace = Some((workspace.weak_handle(), workspace.database_id())); self.workspace = Some((workspace.weak_handle(), workspace.database_id()));
if let Some(workspace) = &workspace.weak_handle().upgrade() { if let Some(workspace) = &workspace.weak_handle().upgrade() {
cx.subscribe(&workspace, |editor, _, event: &workspace::Event, _cx| { cx.subscribe(
if matches!(event, workspace::Event::ModalOpened) { &workspace,
editor.mouse_context_menu.take(); |editor, _, event: &workspace::Event, _cx| match event {
editor.inline_blame_popover.take(); workspace::Event::ModalOpened => {
} editor.mouse_context_menu.take();
}) editor.inline_blame_popover.take();
}
_ => {}
},
)
.detach(); .detach();
} }
} }

View file

@ -1118,15 +1118,17 @@ impl ExtensionStore {
extensions_to_unload.len() - reload_count extensions_to_unload.len() - reload_count
); );
for extension_id in &extensions_to_load { let extension_ids = extensions_to_load
if let Some(extension) = new_index.extensions.get(extension_id) { .iter()
telemetry::event!( .filter_map(|id| {
"Extension Loaded", Some((
extension_id, id.clone(),
version = extension.manifest.version new_index.extensions.get(id)?.manifest.version.clone(),
); ))
} })
} .collect::<Vec<_>>();
telemetry::event!("Extensions Loaded", id_and_versions = extension_ids);
let themes_to_remove = old_index let themes_to_remove = old_index
.themes .themes

View file

@ -293,6 +293,7 @@ pub trait Item: Focusable + EventEmitter<Self::Event> + Render + Sized {
fn deactivated(&mut self, _window: &mut Window, _: &mut Context<Self>) {} fn deactivated(&mut self, _window: &mut Window, _: &mut Context<Self>) {}
fn discarded(&self, _project: Entity<Project>, _window: &mut Window, _cx: &mut Context<Self>) {} fn discarded(&self, _project: Entity<Project>, _window: &mut Window, _cx: &mut Context<Self>) {}
fn on_removed(&self, _cx: &App) {}
fn workspace_deactivated(&mut self, _window: &mut Window, _: &mut Context<Self>) {} fn workspace_deactivated(&mut self, _window: &mut Window, _: &mut Context<Self>) {}
fn navigate(&mut self, _: Box<dyn Any>, _window: &mut Window, _: &mut Context<Self>) -> bool { fn navigate(&mut self, _: Box<dyn Any>, _window: &mut Window, _: &mut Context<Self>) -> bool {
false false
@ -532,6 +533,7 @@ pub trait ItemHandle: 'static + Send {
); );
fn deactivated(&self, window: &mut Window, cx: &mut App); fn deactivated(&self, window: &mut Window, cx: &mut App);
fn discarded(&self, project: Entity<Project>, window: &mut Window, cx: &mut App); fn discarded(&self, project: Entity<Project>, window: &mut Window, cx: &mut App);
fn on_removed(&self, cx: &App);
fn workspace_deactivated(&self, window: &mut Window, cx: &mut App); fn workspace_deactivated(&self, window: &mut Window, cx: &mut App);
fn navigate(&self, data: Box<dyn Any>, window: &mut Window, cx: &mut App) -> bool; fn navigate(&self, data: Box<dyn Any>, window: &mut Window, cx: &mut App) -> bool;
fn item_id(&self) -> EntityId; fn item_id(&self) -> EntityId;
@ -968,6 +970,10 @@ impl<T: Item> ItemHandle for Entity<T> {
self.update(cx, |this, cx| this.deactivated(window, cx)); self.update(cx, |this, cx| this.deactivated(window, cx));
} }
fn on_removed(&self, cx: &App) {
self.read(cx).on_removed(cx);
}
fn workspace_deactivated(&self, window: &mut Window, cx: &mut App) { fn workspace_deactivated(&self, window: &mut Window, cx: &mut App) {
self.update(cx, |this, cx| this.workspace_deactivated(window, cx)); self.update(cx, |this, cx| this.workspace_deactivated(window, cx));
} }

View file

@ -1829,6 +1829,7 @@ impl Pane {
let mode = self.nav_history.mode(); let mode = self.nav_history.mode();
self.nav_history.set_mode(NavigationMode::ClosingItem); self.nav_history.set_mode(NavigationMode::ClosingItem);
item.deactivated(window, cx); item.deactivated(window, cx);
item.on_removed(cx);
self.nav_history.set_mode(mode); self.nav_history.set_mode(mode);
if self.is_active_preview_item(item.item_id()) { if self.is_active_preview_item(item.item_id()) {