Compare commits
7 Commits
vim-syntax
...
telemetry-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fce2b15348 | ||
|
|
bf232e27ca | ||
|
|
6a8b788f89 | ||
|
|
96dd781eb0 | ||
|
|
aae47b0631 | ||
|
|
319eec5bc9 | ||
|
|
7c29f7aac6 |
30
Cargo.lock
generated
30
Cargo.lock
generated
@@ -434,7 +434,7 @@ dependencies = [
|
|||||||
"smallvec",
|
"smallvec",
|
||||||
"smol",
|
"smol",
|
||||||
"strum 0.25.0",
|
"strum 0.25.0",
|
||||||
"telemetry_events",
|
"telemetry",
|
||||||
"terminal",
|
"terminal",
|
||||||
"terminal_view",
|
"terminal_view",
|
||||||
"text",
|
"text",
|
||||||
@@ -2472,7 +2472,7 @@ dependencies = [
|
|||||||
"settings",
|
"settings",
|
||||||
"sha2",
|
"sha2",
|
||||||
"smol",
|
"smol",
|
||||||
"telemetry_events",
|
"telemetry",
|
||||||
"text",
|
"text",
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.69",
|
||||||
"time",
|
"time",
|
||||||
@@ -2653,7 +2653,7 @@ dependencies = [
|
|||||||
"strum 0.25.0",
|
"strum 0.25.0",
|
||||||
"subtle",
|
"subtle",
|
||||||
"supermaven_api",
|
"supermaven_api",
|
||||||
"telemetry_events",
|
"telemetry",
|
||||||
"text",
|
"text",
|
||||||
"theme",
|
"theme",
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.69",
|
||||||
@@ -3366,9 +3366,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ctor"
|
name = "ctor"
|
||||||
version = "0.2.9"
|
version = "0.2.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501"
|
checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.87",
|
"syn 2.0.87",
|
||||||
@@ -4233,6 +4233,7 @@ dependencies = [
|
|||||||
"settings",
|
"settings",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"snippet_provider",
|
"snippet_provider",
|
||||||
|
"telemetry",
|
||||||
"theme",
|
"theme",
|
||||||
"ui",
|
"ui",
|
||||||
"util",
|
"util",
|
||||||
@@ -6557,6 +6558,7 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"smol",
|
"smol",
|
||||||
"strum 0.25.0",
|
"strum 0.25.0",
|
||||||
|
"telemetry",
|
||||||
"ui",
|
"ui",
|
||||||
"util",
|
"util",
|
||||||
]
|
]
|
||||||
@@ -6589,7 +6591,7 @@ dependencies = [
|
|||||||
"settings",
|
"settings",
|
||||||
"smol",
|
"smol",
|
||||||
"strum 0.25.0",
|
"strum 0.25.0",
|
||||||
"telemetry_events",
|
"telemetry",
|
||||||
"theme",
|
"theme",
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.69",
|
||||||
"tiktoken-rs",
|
"tiktoken-rs",
|
||||||
@@ -6720,9 +6722,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.164"
|
version = "0.2.162"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f"
|
checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libdbus-sys"
|
name = "libdbus-sys"
|
||||||
@@ -9882,7 +9884,7 @@ dependencies = [
|
|||||||
"shellexpand 2.1.2",
|
"shellexpand 2.1.2",
|
||||||
"smol",
|
"smol",
|
||||||
"sysinfo",
|
"sysinfo",
|
||||||
"telemetry_events",
|
"telemetry",
|
||||||
"toml 0.8.19",
|
"toml 0.8.19",
|
||||||
"util",
|
"util",
|
||||||
"worktree",
|
"worktree",
|
||||||
@@ -10886,9 +10888,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.133"
|
version = "1.0.132"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377"
|
checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap 2.6.0",
|
"indexmap 2.6.0",
|
||||||
"itoa",
|
"itoa",
|
||||||
@@ -12200,11 +12202,13 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "telemetry_events"
|
name = "telemetry"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"futures 0.3.31",
|
||||||
"semantic_version",
|
"semantic_version",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_json",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -15554,7 +15558,7 @@ dependencies = [
|
|||||||
"tab_switcher",
|
"tab_switcher",
|
||||||
"task",
|
"task",
|
||||||
"tasks_ui",
|
"tasks_ui",
|
||||||
"telemetry_events",
|
"telemetry",
|
||||||
"terminal_view",
|
"terminal_view",
|
||||||
"theme",
|
"theme",
|
||||||
"theme_selector",
|
"theme_selector",
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ members = [
|
|||||||
"crates/tab_switcher",
|
"crates/tab_switcher",
|
||||||
"crates/task",
|
"crates/task",
|
||||||
"crates/tasks_ui",
|
"crates/tasks_ui",
|
||||||
"crates/telemetry_events",
|
"crates/telemetry",
|
||||||
"crates/terminal",
|
"crates/terminal",
|
||||||
"crates/terminal_view",
|
"crates/terminal_view",
|
||||||
"crates/text",
|
"crates/text",
|
||||||
@@ -287,7 +287,7 @@ supermaven_api = { path = "crates/supermaven_api" }
|
|||||||
tab_switcher = { path = "crates/tab_switcher" }
|
tab_switcher = { path = "crates/tab_switcher" }
|
||||||
task = { path = "crates/task" }
|
task = { path = "crates/task" }
|
||||||
tasks_ui = { path = "crates/tasks_ui" }
|
tasks_ui = { path = "crates/tasks_ui" }
|
||||||
telemetry_events = { path = "crates/telemetry_events" }
|
telemetry = { path = "crates/telemetry" }
|
||||||
terminal = { path = "crates/terminal" }
|
terminal = { path = "crates/terminal" }
|
||||||
terminal_view = { path = "crates/terminal_view" }
|
terminal_view = { path = "crates/terminal_view" }
|
||||||
text = { path = "crates/text" }
|
text = { path = "crates/text" }
|
||||||
@@ -354,6 +354,7 @@ derive_more = "0.99.17"
|
|||||||
dirs = "4.0"
|
dirs = "4.0"
|
||||||
ec4rs = "1.1"
|
ec4rs = "1.1"
|
||||||
emojis = "0.6.1"
|
emojis = "0.6.1"
|
||||||
|
erased-serde = "0.4.5"
|
||||||
env_logger = "0.11"
|
env_logger = "0.11"
|
||||||
exec = "0.3.1"
|
exec = "0.3.1"
|
||||||
fancy-regex = "0.14.0"
|
fancy-regex = "0.14.0"
|
||||||
@@ -603,7 +604,7 @@ snippets_ui = { codegen-units = 1 }
|
|||||||
sqlez_macros = { codegen-units = 1 }
|
sqlez_macros = { codegen-units = 1 }
|
||||||
story = { codegen-units = 1 }
|
story = { codegen-units = 1 }
|
||||||
supermaven_api = { codegen-units = 1 }
|
supermaven_api = { codegen-units = 1 }
|
||||||
telemetry_events = { codegen-units = 1 }
|
telemetry = { codegen-units = 1 }
|
||||||
theme_selector = { codegen-units = 1 }
|
theme_selector = { codegen-units = 1 }
|
||||||
time_format = { codegen-units = 1 }
|
time_format = { codegen-units = 1 }
|
||||||
ui_input = { codegen-units = 1 }
|
ui_input = { codegen-units = 1 }
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ similar.workspace = true
|
|||||||
smallvec.workspace = true
|
smallvec.workspace = true
|
||||||
smol.workspace = true
|
smol.workspace = true
|
||||||
strum.workspace = true
|
strum.workspace = true
|
||||||
telemetry_events.workspace = true
|
telemetry.workspace = true
|
||||||
terminal.workspace = true
|
terminal.workspace = true
|
||||||
terminal_view.workspace = true
|
terminal_view.workspace = true
|
||||||
text.workspace = true
|
text.workspace = true
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ use std::{
|
|||||||
sync::Arc,
|
sync::Arc,
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
use telemetry_events::{AssistantEvent, AssistantKind, AssistantPhase};
|
use telemetry::{AssistantEvent, AssistantKind, AssistantPhase};
|
||||||
use text::{BufferSnapshot, ToPoint};
|
use text::{BufferSnapshot, ToPoint};
|
||||||
use util::{post_inc, ResultExt, TryFutureExt};
|
use util::{post_inc, ResultExt, TryFutureExt};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ use std::{
|
|||||||
task::{self, Poll},
|
task::{self, Poll},
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
use telemetry_events::{AssistantEvent, AssistantKind, AssistantPhase};
|
use telemetry::{AssistantEvent, AssistantKind, AssistantPhase};
|
||||||
use terminal_view::terminal_panel::TerminalPanel;
|
use terminal_view::terminal_panel::TerminalPanel;
|
||||||
use text::{OffsetRangeExt, ToPoint as _};
|
use text::{OffsetRangeExt, ToPoint as _};
|
||||||
use theme::ThemeSettings;
|
use theme::ThemeSettings;
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ use std::{
|
|||||||
sync::Arc,
|
sync::Arc,
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
use telemetry_events::{AssistantEvent, AssistantKind, AssistantPhase};
|
use telemetry::{AssistantEvent, AssistantKind, AssistantPhase};
|
||||||
use terminal::Terminal;
|
use terminal::Terminal;
|
||||||
use terminal_view::TerminalView;
|
use terminal_view::TerminalView;
|
||||||
use theme::ThemeSettings;
|
use theme::ThemeSettings;
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ serde_json.workspace = true
|
|||||||
settings.workspace = true
|
settings.workspace = true
|
||||||
sha2.workspace = true
|
sha2.workspace = true
|
||||||
smol.workspace = true
|
smol.workspace = true
|
||||||
telemetry_events.workspace = true
|
telemetry.workspace = true
|
||||||
text.workspace = true
|
text.workspace = true
|
||||||
thiserror.workspace = true
|
thiserror.workspace = true
|
||||||
time.workspace = true
|
time.workspace = true
|
||||||
|
|||||||
@@ -49,8 +49,8 @@ use thiserror::Error;
|
|||||||
use url::Url;
|
use url::Url;
|
||||||
use util::{ResultExt, TryFutureExt};
|
use util::{ResultExt, TryFutureExt};
|
||||||
|
|
||||||
|
pub use ::telemetry::EventBody;
|
||||||
pub use rpc::*;
|
pub use rpc::*;
|
||||||
pub use telemetry_events::Event;
|
|
||||||
pub use user::*;
|
pub use user::*;
|
||||||
|
|
||||||
static ZED_SERVER_URL: LazyLock<Option<String>> =
|
static ZED_SERVER_URL: LazyLock<Option<String>> =
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ use crate::{ChannelId, TelemetrySettings};
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use clock::SystemClock;
|
use clock::SystemClock;
|
||||||
use collections::{HashMap, HashSet};
|
use collections::{HashMap, HashSet};
|
||||||
use futures::Future;
|
use futures::channel::mpsc;
|
||||||
|
use futures::{Future, StreamExt};
|
||||||
use gpui::{AppContext, BackgroundExecutor, Task};
|
use gpui::{AppContext, BackgroundExecutor, Task};
|
||||||
use http_client::{self, AsyncBody, HttpClient, HttpClientWithUrl, Method, Request};
|
use http_client::{self, AsyncBody, HttpClient, HttpClientWithUrl, Method, Request};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
@@ -16,8 +17,8 @@ use std::fs::File;
|
|||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use std::{env, mem, path::PathBuf, sync::Arc, time::Duration};
|
use std::{env, mem, path::PathBuf, sync::Arc, time::Duration};
|
||||||
use telemetry_events::{
|
use telemetry::{
|
||||||
ActionEvent, AppEvent, AssistantEvent, CallEvent, EditEvent, EditorEvent, Event,
|
ActionEvent, AppEvent, AssistantEvent, CallEvent, EditEvent, EditorEvent, EventBody,
|
||||||
EventRequestBody, EventWrapper, ExtensionEvent, InlineCompletionEvent, ReplEvent, SettingEvent,
|
EventRequestBody, EventWrapper, ExtensionEvent, InlineCompletionEvent, ReplEvent, SettingEvent,
|
||||||
};
|
};
|
||||||
use util::{ResultExt, TryFutureExt};
|
use util::{ResultExt, TryFutureExt};
|
||||||
@@ -287,12 +288,29 @@ impl Telemetry {
|
|||||||
session_id: String,
|
session_id: String,
|
||||||
cx: &AppContext,
|
cx: &AppContext,
|
||||||
) {
|
) {
|
||||||
|
let (tx, mut rx) = mpsc::unbounded();
|
||||||
|
|
||||||
let mut state = self.state.lock();
|
let mut state = self.state.lock();
|
||||||
state.system_id = system_id.map(|id| id.into());
|
state.system_id = system_id.map(|id| id.into());
|
||||||
state.installation_id = installation_id.map(|id| id.into());
|
state.installation_id = installation_id.map(|id| id.into());
|
||||||
state.session_id = Some(session_id);
|
state.session_id = Some(session_id);
|
||||||
state.app_version = release_channel::AppVersion::global(cx).to_string();
|
state.app_version = release_channel::AppVersion::global(cx).to_string();
|
||||||
state.os_name = os_name();
|
state.os_name = os_name();
|
||||||
|
drop(state);
|
||||||
|
|
||||||
|
let this = Arc::downgrade(&self);
|
||||||
|
|
||||||
|
::telemetry::init(tx);
|
||||||
|
cx.background_executor()
|
||||||
|
.spawn(async move {
|
||||||
|
while let Some(event) = rx.next().await {
|
||||||
|
let Some(this) = this.upgrade() else {
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
this.report_event(EventBody::Event(event));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn metrics_enabled(self: &Arc<Self>) -> bool {
|
pub fn metrics_enabled(self: &Arc<Self>) -> bool {
|
||||||
@@ -328,7 +346,7 @@ impl Telemetry {
|
|||||||
copilot_enabled_for_language: bool,
|
copilot_enabled_for_language: bool,
|
||||||
is_via_ssh: bool,
|
is_via_ssh: bool,
|
||||||
) {
|
) {
|
||||||
let event = Event::Editor(EditorEvent {
|
let event = EventBody::Editor(EditorEvent {
|
||||||
file_extension,
|
file_extension,
|
||||||
vim_mode,
|
vim_mode,
|
||||||
operation: operation.into(),
|
operation: operation.into(),
|
||||||
@@ -346,7 +364,7 @@ impl Telemetry {
|
|||||||
suggestion_accepted: bool,
|
suggestion_accepted: bool,
|
||||||
file_extension: Option<String>,
|
file_extension: Option<String>,
|
||||||
) {
|
) {
|
||||||
let event = Event::InlineCompletion(InlineCompletionEvent {
|
let event = EventBody::InlineCompletion(InlineCompletionEvent {
|
||||||
provider,
|
provider,
|
||||||
suggestion_accepted,
|
suggestion_accepted,
|
||||||
file_extension,
|
file_extension,
|
||||||
@@ -356,7 +374,7 @@ impl Telemetry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn report_assistant_event(self: &Arc<Self>, event: AssistantEvent) {
|
pub fn report_assistant_event(self: &Arc<Self>, event: AssistantEvent) {
|
||||||
self.report_event(Event::Assistant(event));
|
self.report_event(EventBody::Assistant(event));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn report_call_event(
|
pub fn report_call_event(
|
||||||
@@ -365,7 +383,7 @@ impl Telemetry {
|
|||||||
room_id: Option<u64>,
|
room_id: Option<u64>,
|
||||||
channel_id: Option<ChannelId>,
|
channel_id: Option<ChannelId>,
|
||||||
) {
|
) {
|
||||||
let event = Event::Call(CallEvent {
|
let event = EventBody::Call(CallEvent {
|
||||||
operation: operation.to_string(),
|
operation: operation.to_string(),
|
||||||
room_id,
|
room_id,
|
||||||
channel_id: channel_id.map(|cid| cid.0),
|
channel_id: channel_id.map(|cid| cid.0),
|
||||||
@@ -374,8 +392,8 @@ impl Telemetry {
|
|||||||
self.report_event(event)
|
self.report_event(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn report_app_event(self: &Arc<Self>, operation: String) -> Event {
|
pub fn report_app_event(self: &Arc<Self>, operation: String) -> EventBody {
|
||||||
let event = Event::App(AppEvent { operation });
|
let event = EventBody::App(AppEvent { operation });
|
||||||
|
|
||||||
self.report_event(event.clone());
|
self.report_event(event.clone());
|
||||||
|
|
||||||
@@ -383,7 +401,7 @@ impl Telemetry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn report_setting_event(self: &Arc<Self>, setting: &'static str, value: String) {
|
pub fn report_setting_event(self: &Arc<Self>, setting: &'static str, value: String) {
|
||||||
let event = Event::Setting(SettingEvent {
|
let event = EventBody::Setting(SettingEvent {
|
||||||
setting: setting.to_string(),
|
setting: setting.to_string(),
|
||||||
value,
|
value,
|
||||||
});
|
});
|
||||||
@@ -392,7 +410,7 @@ impl Telemetry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn report_extension_event(self: &Arc<Self>, extension_id: Arc<str>, version: Arc<str>) {
|
pub fn report_extension_event(self: &Arc<Self>, extension_id: Arc<str>, version: Arc<str>) {
|
||||||
self.report_event(Event::Extension(ExtensionEvent {
|
self.report_event(EventBody::Extension(ExtensionEvent {
|
||||||
extension_id,
|
extension_id,
|
||||||
version,
|
version,
|
||||||
}))
|
}))
|
||||||
@@ -404,7 +422,7 @@ impl Telemetry {
|
|||||||
drop(state);
|
drop(state);
|
||||||
|
|
||||||
if let Some((start, end, environment)) = period_data {
|
if let Some((start, end, environment)) = period_data {
|
||||||
let event = Event::Edit(EditEvent {
|
let event = EventBody::Edit(EditEvent {
|
||||||
duration: end
|
duration: end
|
||||||
.saturating_duration_since(start)
|
.saturating_duration_since(start)
|
||||||
.min(Duration::from_secs(60 * 60 * 24))
|
.min(Duration::from_secs(60 * 60 * 24))
|
||||||
@@ -418,7 +436,7 @@ impl Telemetry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn report_action_event(self: &Arc<Self>, source: &'static str, action: String) {
|
pub fn report_action_event(self: &Arc<Self>, source: &'static str, action: String) {
|
||||||
let event = Event::Action(ActionEvent {
|
let event = EventBody::Action(ActionEvent {
|
||||||
source: source.to_string(),
|
source: source.to_string(),
|
||||||
action,
|
action,
|
||||||
});
|
});
|
||||||
@@ -478,7 +496,7 @@ impl Telemetry {
|
|||||||
kernel_status: String,
|
kernel_status: String,
|
||||||
repl_session_id: String,
|
repl_session_id: String,
|
||||||
) {
|
) {
|
||||||
let event = Event::Repl(ReplEvent {
|
let event = EventBody::Repl(ReplEvent {
|
||||||
kernel_language,
|
kernel_language,
|
||||||
kernel_status,
|
kernel_status,
|
||||||
repl_session_id,
|
repl_session_id,
|
||||||
@@ -487,7 +505,7 @@ impl Telemetry {
|
|||||||
self.report_event(event)
|
self.report_event(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn report_event(self: &Arc<Self>, event: Event) {
|
fn report_event(self: &Arc<Self>, event: EventBody) {
|
||||||
let mut state = self.state.lock();
|
let mut state = self.state.lock();
|
||||||
|
|
||||||
if !state.settings.metrics {
|
if !state.settings.metrics {
|
||||||
@@ -669,7 +687,7 @@ mod tests {
|
|||||||
let event = telemetry.report_app_event(operation.clone());
|
let event = telemetry.report_app_event(operation.clone());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
event,
|
event,
|
||||||
Event::App(AppEvent {
|
EventBody::App(AppEvent {
|
||||||
operation: operation.clone(),
|
operation: operation.clone(),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -685,7 +703,7 @@ mod tests {
|
|||||||
let event = telemetry.report_app_event(operation.clone());
|
let event = telemetry.report_app_event(operation.clone());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
event,
|
event,
|
||||||
Event::App(AppEvent {
|
EventBody::App(AppEvent {
|
||||||
operation: operation.clone(),
|
operation: operation.clone(),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -701,7 +719,7 @@ mod tests {
|
|||||||
let event = telemetry.report_app_event(operation.clone());
|
let event = telemetry.report_app_event(operation.clone());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
event,
|
event,
|
||||||
Event::App(AppEvent {
|
EventBody::App(AppEvent {
|
||||||
operation: operation.clone(),
|
operation: operation.clone(),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -718,7 +736,7 @@ mod tests {
|
|||||||
let event = telemetry.report_app_event(operation.clone());
|
let event = telemetry.report_app_event(operation.clone());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
event,
|
event,
|
||||||
Event::App(AppEvent {
|
EventBody::App(AppEvent {
|
||||||
operation: operation.clone(),
|
operation: operation.clone(),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -752,7 +770,7 @@ mod tests {
|
|||||||
let event = telemetry.report_app_event(operation.clone());
|
let event = telemetry.report_app_event(operation.clone());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
event,
|
event,
|
||||||
Event::App(AppEvent {
|
EventBody::App(AppEvent {
|
||||||
operation: operation.clone(),
|
operation: operation.clone(),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ sqlx = { version = "0.8", features = ["runtime-tokio-rustls", "postgres", "json"
|
|||||||
strum.workspace = true
|
strum.workspace = true
|
||||||
subtle.workspace = true
|
subtle.workspace = true
|
||||||
supermaven_api.workspace = true
|
supermaven_api.workspace = true
|
||||||
telemetry_events.workspace = true
|
telemetry.workspace = true
|
||||||
text.workspace = true
|
text.workspace = true
|
||||||
thiserror.workspace = true
|
thiserror.workspace = true
|
||||||
time.workspace = true
|
time.workspace = true
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ use serde::{Deserialize, Serialize, Serializer};
|
|||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
use std::sync::{Arc, OnceLock};
|
use std::sync::{Arc, OnceLock};
|
||||||
use telemetry_events::{
|
use telemetry::{
|
||||||
ActionEvent, AppEvent, AssistantEvent, CallEvent, CpuEvent, EditEvent, EditorEvent, Event,
|
ActionEvent, AppEvent, AssistantEvent, CallEvent, CpuEvent, EditEvent, EditorEvent, EventBody,
|
||||||
EventRequestBody, EventWrapper, ExtensionEvent, InlineCompletionEvent, MemoryEvent, Panic,
|
EventRequestBody, EventWrapper, ExtensionEvent, InlineCompletionEvent, MemoryEvent, Panic,
|
||||||
ReplEvent, SettingEvent,
|
ReplEvent, SettingEvent,
|
||||||
};
|
};
|
||||||
@@ -240,7 +240,7 @@ pub async fn post_hang(
|
|||||||
.ok();
|
.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
let report: telemetry_events::HangReport = serde_json::from_slice(&body).map_err(|err| {
|
let report: telemetry::HangReport = serde_json::from_slice(&body).map_err(|err| {
|
||||||
log::error!("can't parse report json: {err}");
|
log::error!("can't parse report json: {err}");
|
||||||
Error::Internal(anyhow!(err))
|
Error::Internal(anyhow!(err))
|
||||||
})?;
|
})?;
|
||||||
@@ -283,7 +283,7 @@ pub async fn post_panic(
|
|||||||
))?;
|
))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let report: telemetry_events::PanicRequest = serde_json::from_slice(&body)
|
let report: telemetry::PanicRequest = serde_json::from_slice(&body)
|
||||||
.map_err(|_| Error::http(StatusCode::BAD_REQUEST, "invalid json".into()))?;
|
.map_err(|_| Error::http(StatusCode::BAD_REQUEST, "invalid json".into()))?;
|
||||||
let panic = report.panic;
|
let panic = report.panic;
|
||||||
|
|
||||||
@@ -400,7 +400,7 @@ pub async fn post_events(
|
|||||||
|
|
||||||
let checksum_matched = checksum == expected;
|
let checksum_matched = checksum == expected;
|
||||||
|
|
||||||
let request_body: telemetry_events::EventRequestBody =
|
let request_body: telemetry::EventRequestBody =
|
||||||
serde_json::from_slice(&body).map_err(|err| {
|
serde_json::from_slice(&body).map_err(|err| {
|
||||||
log::error!("can't parse event json: {err}");
|
log::error!("can't parse event json: {err}");
|
||||||
Error::Internal(anyhow!(err))
|
Error::Internal(anyhow!(err))
|
||||||
@@ -445,7 +445,8 @@ pub async fn post_events(
|
|||||||
|
|
||||||
for wrapper in &request_body.events {
|
for wrapper in &request_body.events {
|
||||||
match &wrapper.event {
|
match &wrapper.event {
|
||||||
Event::Editor(event) => to_upload.editor_events.push(EditorEventRow::from_event(
|
EventBody::Event(_) => continue,
|
||||||
|
EventBody::Editor(event) => to_upload.editor_events.push(EditorEventRow::from_event(
|
||||||
event.clone(),
|
event.clone(),
|
||||||
wrapper,
|
wrapper,
|
||||||
&request_body,
|
&request_body,
|
||||||
@@ -453,7 +454,7 @@ pub async fn post_events(
|
|||||||
country_code.clone(),
|
country_code.clone(),
|
||||||
checksum_matched,
|
checksum_matched,
|
||||||
)),
|
)),
|
||||||
Event::InlineCompletion(event) => {
|
EventBody::InlineCompletion(event) => {
|
||||||
to_upload
|
to_upload
|
||||||
.inline_completion_events
|
.inline_completion_events
|
||||||
.push(InlineCompletionEventRow::from_event(
|
.push(InlineCompletionEventRow::from_event(
|
||||||
@@ -465,14 +466,14 @@ pub async fn post_events(
|
|||||||
checksum_matched,
|
checksum_matched,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
Event::Call(event) => to_upload.call_events.push(CallEventRow::from_event(
|
EventBody::Call(event) => to_upload.call_events.push(CallEventRow::from_event(
|
||||||
event.clone(),
|
event.clone(),
|
||||||
wrapper,
|
wrapper,
|
||||||
&request_body,
|
&request_body,
|
||||||
first_event_at,
|
first_event_at,
|
||||||
checksum_matched,
|
checksum_matched,
|
||||||
)),
|
)),
|
||||||
Event::Assistant(event) => {
|
EventBody::Assistant(event) => {
|
||||||
to_upload
|
to_upload
|
||||||
.assistant_events
|
.assistant_events
|
||||||
.push(AssistantEventRow::from_event(
|
.push(AssistantEventRow::from_event(
|
||||||
@@ -483,36 +484,38 @@ pub async fn post_events(
|
|||||||
checksum_matched,
|
checksum_matched,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
Event::Cpu(_) | Event::Memory(_) => continue,
|
EventBody::Cpu(_) | EventBody::Memory(_) => continue,
|
||||||
Event::App(event) => to_upload.app_events.push(AppEventRow::from_event(
|
EventBody::App(event) => to_upload.app_events.push(AppEventRow::from_event(
|
||||||
event.clone(),
|
event.clone(),
|
||||||
wrapper,
|
wrapper,
|
||||||
&request_body,
|
&request_body,
|
||||||
first_event_at,
|
first_event_at,
|
||||||
checksum_matched,
|
checksum_matched,
|
||||||
)),
|
)),
|
||||||
Event::Setting(event) => to_upload.setting_events.push(SettingEventRow::from_event(
|
EventBody::Setting(event) => {
|
||||||
|
to_upload.setting_events.push(SettingEventRow::from_event(
|
||||||
|
event.clone(),
|
||||||
|
wrapper,
|
||||||
|
&request_body,
|
||||||
|
first_event_at,
|
||||||
|
checksum_matched,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
EventBody::Edit(event) => to_upload.edit_events.push(EditEventRow::from_event(
|
||||||
event.clone(),
|
event.clone(),
|
||||||
wrapper,
|
wrapper,
|
||||||
&request_body,
|
&request_body,
|
||||||
first_event_at,
|
first_event_at,
|
||||||
checksum_matched,
|
checksum_matched,
|
||||||
)),
|
)),
|
||||||
Event::Edit(event) => to_upload.edit_events.push(EditEventRow::from_event(
|
EventBody::Action(event) => to_upload.action_events.push(ActionEventRow::from_event(
|
||||||
event.clone(),
|
event.clone(),
|
||||||
wrapper,
|
wrapper,
|
||||||
&request_body,
|
&request_body,
|
||||||
first_event_at,
|
first_event_at,
|
||||||
checksum_matched,
|
checksum_matched,
|
||||||
)),
|
)),
|
||||||
Event::Action(event) => to_upload.action_events.push(ActionEventRow::from_event(
|
EventBody::Extension(event) => {
|
||||||
event.clone(),
|
|
||||||
wrapper,
|
|
||||||
&request_body,
|
|
||||||
first_event_at,
|
|
||||||
checksum_matched,
|
|
||||||
)),
|
|
||||||
Event::Extension(event) => {
|
|
||||||
let metadata = app
|
let metadata = app
|
||||||
.db
|
.db
|
||||||
.get_extension_version(&event.extension_id, &event.version)
|
.get_extension_version(&event.extension_id, &event.version)
|
||||||
@@ -528,7 +531,7 @@ pub async fn post_events(
|
|||||||
checksum_matched,
|
checksum_matched,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
Event::Repl(event) => to_upload.repl_events.push(ReplEventRow::from_event(
|
EventBody::Repl(event) => to_upload.repl_events.push(ReplEventRow::from_event(
|
||||||
event.clone(),
|
event.clone(),
|
||||||
wrapper,
|
wrapper,
|
||||||
&request_body,
|
&request_body,
|
||||||
@@ -1387,7 +1390,7 @@ fn for_snowflake(
|
|||||||
let timestamp =
|
let timestamp =
|
||||||
first_event_at + Duration::milliseconds(event.milliseconds_since_first_event);
|
first_event_at + Duration::milliseconds(event.milliseconds_since_first_event);
|
||||||
let (event_type, mut event_properties) = match &event.event {
|
let (event_type, mut event_properties) = match &event.event {
|
||||||
Event::Editor(e) => (
|
EventBody::Editor(e) => (
|
||||||
match e.operation.as_str() {
|
match e.operation.as_str() {
|
||||||
"open" => "Editor Opened".to_string(),
|
"open" => "Editor Opened".to_string(),
|
||||||
"save" => "Editor Saved".to_string(),
|
"save" => "Editor Saved".to_string(),
|
||||||
@@ -1395,7 +1398,8 @@ fn for_snowflake(
|
|||||||
},
|
},
|
||||||
serde_json::to_value(e).unwrap(),
|
serde_json::to_value(e).unwrap(),
|
||||||
),
|
),
|
||||||
Event::InlineCompletion(e) => (
|
EventBody::Event(e) => (e.name.clone(), serde_json::to_value(&e.properties).unwrap()),
|
||||||
|
EventBody::InlineCompletion(e) => (
|
||||||
format!(
|
format!(
|
||||||
"Inline Completion {}",
|
"Inline Completion {}",
|
||||||
if e.suggestion_accepted {
|
if e.suggestion_accepted {
|
||||||
@@ -1406,7 +1410,7 @@ fn for_snowflake(
|
|||||||
),
|
),
|
||||||
serde_json::to_value(e).unwrap(),
|
serde_json::to_value(e).unwrap(),
|
||||||
),
|
),
|
||||||
Event::Call(e) => {
|
EventBody::Call(e) => {
|
||||||
let event_type = match e.operation.trim() {
|
let event_type = match e.operation.trim() {
|
||||||
"unshare project" => "Project Unshared".to_string(),
|
"unshare project" => "Project Unshared".to_string(),
|
||||||
"open channel notes" => "Channel Notes Opened".to_string(),
|
"open channel notes" => "Channel Notes Opened".to_string(),
|
||||||
@@ -1427,21 +1431,21 @@ fn for_snowflake(
|
|||||||
|
|
||||||
(event_type, serde_json::to_value(e).unwrap())
|
(event_type, serde_json::to_value(e).unwrap())
|
||||||
}
|
}
|
||||||
Event::Assistant(e) => (
|
EventBody::Assistant(e) => (
|
||||||
match e.phase {
|
match e.phase {
|
||||||
telemetry_events::AssistantPhase::Response => "Assistant Responded".to_string(),
|
telemetry::AssistantPhase::Response => "Assistant Responded".to_string(),
|
||||||
telemetry_events::AssistantPhase::Invoked => "Assistant Invoked".to_string(),
|
telemetry::AssistantPhase::Invoked => "Assistant Invoked".to_string(),
|
||||||
telemetry_events::AssistantPhase::Accepted => {
|
telemetry::AssistantPhase::Accepted => {
|
||||||
"Assistant Response Accepted".to_string()
|
"Assistant Response Accepted".to_string()
|
||||||
}
|
}
|
||||||
telemetry_events::AssistantPhase::Rejected => {
|
telemetry::AssistantPhase::Rejected => {
|
||||||
"Assistant Response Rejected".to_string()
|
"Assistant Response Rejected".to_string()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
serde_json::to_value(e).unwrap(),
|
serde_json::to_value(e).unwrap(),
|
||||||
),
|
),
|
||||||
Event::Cpu(_) | Event::Memory(_) => return None,
|
EventBody::Cpu(_) | EventBody::Memory(_) => return None,
|
||||||
Event::App(e) => {
|
EventBody::App(e) => {
|
||||||
let mut properties = json!({});
|
let mut properties = json!({});
|
||||||
let event_type = match e.operation.trim() {
|
let event_type = match e.operation.trim() {
|
||||||
"extensions: install extension" => "Extension Installed".to_string(),
|
"extensions: install extension" => "Extension Installed".to_string(),
|
||||||
@@ -1522,23 +1526,23 @@ fn for_snowflake(
|
|||||||
};
|
};
|
||||||
(event_type, properties)
|
(event_type, properties)
|
||||||
}
|
}
|
||||||
Event::Setting(e) => (
|
EventBody::Setting(e) => (
|
||||||
"Settings Changed".to_string(),
|
"Settings Changed".to_string(),
|
||||||
serde_json::to_value(e).unwrap(),
|
serde_json::to_value(e).unwrap(),
|
||||||
),
|
),
|
||||||
Event::Extension(e) => (
|
EventBody::Extension(e) => (
|
||||||
"Extension Loaded".to_string(),
|
"Extension Loaded".to_string(),
|
||||||
serde_json::to_value(e).unwrap(),
|
serde_json::to_value(e).unwrap(),
|
||||||
),
|
),
|
||||||
Event::Edit(e) => (
|
EventBody::Edit(e) => (
|
||||||
"Editor Edited".to_string(),
|
"Editor Edited".to_string(),
|
||||||
serde_json::to_value(e).unwrap(),
|
serde_json::to_value(e).unwrap(),
|
||||||
),
|
),
|
||||||
Event::Action(e) => (
|
EventBody::Action(e) => (
|
||||||
"Action Invoked".to_string(),
|
"Action Invoked".to_string(),
|
||||||
serde_json::to_value(e).unwrap(),
|
serde_json::to_value(e).unwrap(),
|
||||||
),
|
),
|
||||||
Event::Repl(e) => (
|
EventBody::Repl(e) => (
|
||||||
"Kernel Status Changed".to_string(),
|
"Kernel Status Changed".to_string(),
|
||||||
serde_json::to_value(e).unwrap(),
|
serde_json::to_value(e).unwrap(),
|
||||||
),
|
),
|
||||||
@@ -1578,7 +1582,7 @@ fn for_snowflake(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
struct SnowflakeRow {
|
struct SnowflakeRow {
|
||||||
pub time: chrono::DateTime<chrono::Utc>,
|
pub time: chrono::DateTime<chrono::Utc>,
|
||||||
pub user_id: Option<String>,
|
pub user_id: Option<String>,
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ serde.workspace = true
|
|||||||
settings.workspace = true
|
settings.workspace = true
|
||||||
smallvec.workspace = true
|
smallvec.workspace = true
|
||||||
snippet_provider.workspace = true
|
snippet_provider.workspace = true
|
||||||
|
telemetry.workspace = true
|
||||||
theme.workspace = true
|
theme.workspace = true
|
||||||
ui.workspace = true
|
ui.workspace = true
|
||||||
util.workspace = true
|
util.workspace = true
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use client::telemetry::Telemetry;
|
|
||||||
use gpui::{AnyElement, Div, StyleRefinement};
|
use gpui::{AnyElement, Div, StyleRefinement};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use ui::{prelude::*, ButtonLike};
|
use ui::{prelude::*, ButtonLike};
|
||||||
@@ -8,17 +5,15 @@ use ui::{prelude::*, ButtonLike};
|
|||||||
#[derive(IntoElement)]
|
#[derive(IntoElement)]
|
||||||
pub struct FeatureUpsell {
|
pub struct FeatureUpsell {
|
||||||
base: Div,
|
base: Div,
|
||||||
telemetry: Arc<Telemetry>,
|
|
||||||
text: SharedString,
|
text: SharedString,
|
||||||
docs_url: Option<SharedString>,
|
docs_url: Option<SharedString>,
|
||||||
children: SmallVec<[AnyElement; 2]>,
|
children: SmallVec<[AnyElement; 2]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FeatureUpsell {
|
impl FeatureUpsell {
|
||||||
pub fn new(telemetry: Arc<Telemetry>, text: impl Into<SharedString>) -> Self {
|
pub fn new(text: impl Into<SharedString>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
base: h_flex(),
|
base: h_flex(),
|
||||||
telemetry,
|
|
||||||
text: text.into(),
|
text: text.into(),
|
||||||
docs_url: None,
|
docs_url: None,
|
||||||
children: SmallVec::new(),
|
children: SmallVec::new(),
|
||||||
@@ -67,12 +62,13 @@ impl RenderOnce for FeatureUpsell {
|
|||||||
.child(Icon::new(IconName::ArrowUpRight)),
|
.child(Icon::new(IconName::ArrowUpRight)),
|
||||||
)
|
)
|
||||||
.on_click({
|
.on_click({
|
||||||
let telemetry = self.telemetry.clone();
|
|
||||||
let docs_url = docs_url.clone();
|
let docs_url = docs_url.clone();
|
||||||
move |_event, cx| {
|
move |_event, cx| {
|
||||||
telemetry.report_app_event(format!(
|
telemetry::event!(
|
||||||
"feature upsell: viewed docs ({docs_url})"
|
"Documentation Viewed",
|
||||||
));
|
source = "Feature Upsell",
|
||||||
|
url = docs_url
|
||||||
|
);
|
||||||
cx.open_url(&docs_url)
|
cx.open_url(&docs_url)
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -975,19 +975,16 @@ impl ExtensionsPage {
|
|||||||
let upsells_count = self.upsells.len();
|
let upsells_count = self.upsells.len();
|
||||||
|
|
||||||
v_flex().children(self.upsells.iter().enumerate().map(|(ix, feature)| {
|
v_flex().children(self.upsells.iter().enumerate().map(|(ix, feature)| {
|
||||||
let telemetry = self.telemetry.clone();
|
|
||||||
let upsell = match feature {
|
let upsell = match feature {
|
||||||
Feature::Git => FeatureUpsell::new(
|
Feature::Git => FeatureUpsell::new(
|
||||||
telemetry,
|
|
||||||
"Zed comes with basic Git support. More Git features are coming in the future.",
|
"Zed comes with basic Git support. More Git features are coming in the future.",
|
||||||
)
|
)
|
||||||
.docs_url("https://zed.dev/docs/git"),
|
.docs_url("https://zed.dev/docs/git"),
|
||||||
Feature::OpenIn => FeatureUpsell::new(
|
Feature::OpenIn => FeatureUpsell::new(
|
||||||
telemetry,
|
|
||||||
"Zed supports linking to a source line on GitHub and others.",
|
"Zed supports linking to a source line on GitHub and others.",
|
||||||
)
|
)
|
||||||
.docs_url("https://zed.dev/docs/git#git-integrations"),
|
.docs_url("https://zed.dev/docs/git#git-integrations"),
|
||||||
Feature::Vim => FeatureUpsell::new(telemetry, "Vim support is built-in to Zed!")
|
Feature::Vim => FeatureUpsell::new("Vim support is built-in to Zed!")
|
||||||
.docs_url("https://zed.dev/docs/vim")
|
.docs_url("https://zed.dev/docs/vim")
|
||||||
.child(CheckboxWithLabel::new(
|
.child(CheckboxWithLabel::new(
|
||||||
"enable-vim",
|
"enable-vim",
|
||||||
@@ -1007,36 +1004,22 @@ impl ExtensionsPage {
|
|||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
)),
|
)),
|
||||||
Feature::LanguageBash => {
|
Feature::LanguageBash => FeatureUpsell::new("Shell support is built-in to Zed!")
|
||||||
FeatureUpsell::new(telemetry, "Shell support is built-in to Zed!")
|
.docs_url("https://zed.dev/docs/languages/bash"),
|
||||||
.docs_url("https://zed.dev/docs/languages/bash")
|
Feature::LanguageC => FeatureUpsell::new("C support is built-in to Zed!")
|
||||||
}
|
.docs_url("https://zed.dev/docs/languages/c"),
|
||||||
Feature::LanguageC => {
|
Feature::LanguageCpp => FeatureUpsell::new("C++ support is built-in to Zed!")
|
||||||
FeatureUpsell::new(telemetry, "C support is built-in to Zed!")
|
.docs_url("https://zed.dev/docs/languages/cpp"),
|
||||||
.docs_url("https://zed.dev/docs/languages/c")
|
Feature::LanguageGo => FeatureUpsell::new("Go support is built-in to Zed!")
|
||||||
}
|
.docs_url("https://zed.dev/docs/languages/go"),
|
||||||
Feature::LanguageCpp => {
|
Feature::LanguagePython => FeatureUpsell::new("Python support is built-in to Zed!")
|
||||||
FeatureUpsell::new(telemetry, "C++ support is built-in to Zed!")
|
.docs_url("https://zed.dev/docs/languages/python"),
|
||||||
.docs_url("https://zed.dev/docs/languages/cpp")
|
Feature::LanguageReact => FeatureUpsell::new("React support is built-in to Zed!")
|
||||||
}
|
.docs_url("https://zed.dev/docs/languages/typescript"),
|
||||||
Feature::LanguageGo => {
|
Feature::LanguageRust => FeatureUpsell::new("Rust support is built-in to Zed!")
|
||||||
FeatureUpsell::new(telemetry, "Go support is built-in to Zed!")
|
.docs_url("https://zed.dev/docs/languages/rust"),
|
||||||
.docs_url("https://zed.dev/docs/languages/go")
|
|
||||||
}
|
|
||||||
Feature::LanguagePython => {
|
|
||||||
FeatureUpsell::new(telemetry, "Python support is built-in to Zed!")
|
|
||||||
.docs_url("https://zed.dev/docs/languages/python")
|
|
||||||
}
|
|
||||||
Feature::LanguageReact => {
|
|
||||||
FeatureUpsell::new(telemetry, "React support is built-in to Zed!")
|
|
||||||
.docs_url("https://zed.dev/docs/languages/typescript")
|
|
||||||
}
|
|
||||||
Feature::LanguageRust => {
|
|
||||||
FeatureUpsell::new(telemetry, "Rust support is built-in to Zed!")
|
|
||||||
.docs_url("https://zed.dev/docs/languages/rust")
|
|
||||||
}
|
|
||||||
Feature::LanguageTypescript => {
|
Feature::LanguageTypescript => {
|
||||||
FeatureUpsell::new(telemetry, "Typescript support is built-in to Zed!")
|
FeatureUpsell::new("Typescript support is built-in to Zed!")
|
||||||
.docs_url("https://zed.dev/docs/languages/typescript")
|
.docs_url("https://zed.dev/docs/languages/typescript")
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ serde.workspace = true
|
|||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
smol.workspace = true
|
smol.workspace = true
|
||||||
strum.workspace = true
|
strum.workspace = true
|
||||||
|
telemetry.workspace = true
|
||||||
ui.workspace = true
|
ui.workspace = true
|
||||||
util.workspace = true
|
util.workspace = true
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ serde_json.workspace = true
|
|||||||
settings.workspace = true
|
settings.workspace = true
|
||||||
smol.workspace = true
|
smol.workspace = true
|
||||||
strum.workspace = true
|
strum.workspace = true
|
||||||
telemetry_events.workspace = true
|
telemetry.workspace = true
|
||||||
theme.workspace = true
|
theme.workspace = true
|
||||||
thiserror.workspace = true
|
thiserror.workspace = true
|
||||||
tiktoken-rs.workspace = true
|
tiktoken-rs.workspace = true
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use gpui::BackgroundExecutor;
|
|||||||
use http_client::{AsyncBody, HttpClient, Method, Request as HttpRequest};
|
use http_client::{AsyncBody, HttpClient, Method, Request as HttpRequest};
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use telemetry_events::{AssistantEvent, AssistantKind, AssistantPhase};
|
use telemetry::{AssistantEvent, AssistantKind, AssistantPhase};
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
|
|
||||||
use crate::provider::anthropic::PROVIDER_ID as ANTHROPIC_PROVIDER_ID;
|
use crate::provider::anthropic::PROVIDER_ID as ANTHROPIC_PROVIDER_ID;
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ settings.workspace = true
|
|||||||
shellexpand.workspace = true
|
shellexpand.workspace = true
|
||||||
smol.workspace = true
|
smol.workspace = true
|
||||||
sysinfo.workspace = true
|
sysinfo.workspace = true
|
||||||
telemetry_events.workspace = true
|
telemetry.workspace = true
|
||||||
util.workspace = true
|
util.workspace = true
|
||||||
worktree.workspace = true
|
worktree.workspace = true
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ use settings::{watch_config_file, Settings, SettingsStore};
|
|||||||
use smol::channel::{Receiver, Sender};
|
use smol::channel::{Receiver, Sender};
|
||||||
use smol::io::AsyncReadExt;
|
use smol::io::AsyncReadExt;
|
||||||
|
|
||||||
|
use ::telemetry::LocationData;
|
||||||
use smol::Async;
|
use smol::Async;
|
||||||
use smol::{net::unix::UnixListener, stream::StreamExt as _};
|
use smol::{net::unix::UnixListener, stream::StreamExt as _};
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
@@ -40,7 +41,6 @@ use std::{
|
|||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
use telemetry_events::LocationData;
|
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
|
|
||||||
fn init_logging_proxy() {
|
fn init_logging_proxy() {
|
||||||
@@ -147,7 +147,7 @@ fn init_panic_hook() {
|
|||||||
(&backtrace).join("\n")
|
(&backtrace).join("\n")
|
||||||
);
|
);
|
||||||
|
|
||||||
let panic_data = telemetry_events::Panic {
|
let panic_data = ::telemetry::Panic {
|
||||||
thread: thread_name.into(),
|
thread: thread_name.into(),
|
||||||
payload: payload.clone(),
|
payload: payload.clone(),
|
||||||
location_data: info.location().map(|location| LocationData {
|
location_data: info.location().map(|location| LocationData {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "telemetry_events"
|
name = "telemetry"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
publish = false
|
publish = false
|
||||||
@@ -9,8 +9,10 @@ license = "GPL-3.0-or-later"
|
|||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
path = "src/telemetry_events.rs"
|
path = "src/telemetry.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
semantic_version.workspace = true
|
semantic_version.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
|
futures.workspace = true
|
||||||
|
serde_json = { workspace = true, features = ["raw_value"] }
|
||||||
@@ -1,8 +1,15 @@
|
|||||||
//! See [Telemetry in Zed](https://zed.dev/docs/telemetry) for additional information.
|
//! See [Telemetry in Zed](https://zed.dev/docs/telemetry) for additional information.
|
||||||
|
|
||||||
|
use futures::channel::mpsc;
|
||||||
use semantic_version::SemanticVersion;
|
use semantic_version::SemanticVersion;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{fmt::Display, sync::Arc, time::Duration};
|
pub use serde_json;
|
||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
fmt::Display,
|
||||||
|
sync::{Arc, OnceLock},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub struct EventRequestBody {
|
pub struct EventRequestBody {
|
||||||
@@ -32,14 +39,14 @@ impl EventRequestBody {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
|
||||||
pub struct EventWrapper {
|
pub struct EventWrapper {
|
||||||
pub signed_in: bool,
|
pub signed_in: bool,
|
||||||
/// Duration between this event's timestamp and the timestamp of the first event in the current batch
|
/// Duration between this event's timestamp and the timestamp of the first event in the current batch
|
||||||
pub milliseconds_since_first_event: i64,
|
pub milliseconds_since_first_event: i64,
|
||||||
/// The event itself
|
/// The event itself
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub event: Event,
|
pub event: EventBody,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
@@ -90,7 +97,8 @@ impl Display for AssistantPhase {
|
|||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
pub enum Event {
|
pub enum EventBody {
|
||||||
|
Event(Event),
|
||||||
Editor(EditorEvent),
|
Editor(EditorEvent),
|
||||||
InlineCompletion(InlineCompletionEvent),
|
InlineCompletion(InlineCompletionEvent),
|
||||||
Call(CallEvent),
|
Call(CallEvent),
|
||||||
@@ -265,3 +273,60 @@ pub struct Panic {
|
|||||||
pub struct PanicRequest {
|
pub struct PanicRequest {
|
||||||
pub panic: Panic,
|
pub panic: Panic,
|
||||||
}
|
}
|
||||||
|
/// Macro to create telemetry events and send them to the telemetry queue.
|
||||||
|
///
|
||||||
|
/// By convention, the name should be "Noun Verbed", e.g. "Keymap Changed"
|
||||||
|
/// or "Project Diagnostics Opened".
|
||||||
|
///
|
||||||
|
/// The properties can be any value that implements serde::Serialize.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// telemetry::event!("Keymap Changed", version = "1.0.0");
|
||||||
|
/// telemetry::event!("Documentation Viewed", url, source = "Extension Upsell");
|
||||||
|
/// ```
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! event {
|
||||||
|
($name:expr, $($key:ident $(= $value:expr)?),+ $(,)?) => {{
|
||||||
|
let event = $crate::Event {
|
||||||
|
name: $name.to_string(),
|
||||||
|
properties: std::collections::HashMap::from([
|
||||||
|
$(
|
||||||
|
(stringify!($key).to_string(),
|
||||||
|
$crate::serde_json::value::to_value(&$crate::serialize_property!($key $(= $value)?))
|
||||||
|
.unwrap_or_else(|_| $crate::serde_json::to_value(&()).unwrap())
|
||||||
|
),
|
||||||
|
)+
|
||||||
|
]),
|
||||||
|
};
|
||||||
|
$crate::send_event(event);
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! serialize_property {
|
||||||
|
($key:ident) => {
|
||||||
|
$key
|
||||||
|
};
|
||||||
|
($key:ident = $value:expr) => {
|
||||||
|
$value
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||||
|
pub struct Event {
|
||||||
|
pub name: String,
|
||||||
|
pub properties: HashMap<String, serde_json::Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_event(event: Event) {
|
||||||
|
if let Some(queue) = TELEMETRY_QUEUE.get() {
|
||||||
|
queue.unbounded_send(event).ok();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(tx: mpsc::UnboundedSender<Event>) {
|
||||||
|
TELEMETRY_QUEUE.set(tx).ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
static TELEMETRY_QUEUE: OnceLock<mpsc::UnboundedSender<Event>> = OnceLock::new();
|
||||||
@@ -106,7 +106,7 @@ sysinfo.workspace = true
|
|||||||
tab_switcher.workspace = true
|
tab_switcher.workspace = true
|
||||||
task.workspace = true
|
task.workspace = true
|
||||||
tasks_ui.workspace = true
|
tasks_ui.workspace = true
|
||||||
telemetry_events.workspace = true
|
telemetry.workspace = true
|
||||||
terminal_view.workspace = true
|
terminal_view.workspace = true
|
||||||
theme.workspace = true
|
theme.workspace = true
|
||||||
theme_selector.workspace = true
|
theme_selector.workspace = true
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ use db::kvp::KEY_VALUE_STORE;
|
|||||||
use gpui::{AppContext, SemanticVersion};
|
use gpui::{AppContext, SemanticVersion};
|
||||||
use http_client::{HttpRequestExt, Method};
|
use http_client::{HttpRequestExt, Method};
|
||||||
|
|
||||||
|
use ::telemetry::LocationData;
|
||||||
|
use ::telemetry::Panic;
|
||||||
|
use ::telemetry::PanicRequest;
|
||||||
use http_client::{self, HttpClient, HttpClientWithUrl};
|
use http_client::{self, HttpClient, HttpClientWithUrl};
|
||||||
use paths::{crashes_dir, crashes_retired_dir};
|
use paths::{crashes_dir, crashes_retired_dir};
|
||||||
use project::Project;
|
use project::Project;
|
||||||
@@ -19,9 +22,6 @@ use std::{
|
|||||||
sync::{atomic::Ordering, Arc},
|
sync::{atomic::Ordering, Arc},
|
||||||
};
|
};
|
||||||
use std::{io::Write, panic, sync::atomic::AtomicU32, thread};
|
use std::{io::Write, panic, sync::atomic::AtomicU32, thread};
|
||||||
use telemetry_events::LocationData;
|
|
||||||
use telemetry_events::Panic;
|
|
||||||
use telemetry_events::PanicRequest;
|
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
|
|
||||||
@@ -90,7 +90,7 @@ pub fn init_panic_hook(
|
|||||||
backtrace.drain(0..=ix);
|
backtrace.drain(0..=ix);
|
||||||
}
|
}
|
||||||
|
|
||||||
let panic_data = telemetry_events::Panic {
|
let panic_data = ::telemetry::Panic {
|
||||||
thread: thread_name.into(),
|
thread: thread_name.into(),
|
||||||
payload,
|
payload,
|
||||||
location_data: info.location().map(|location| LocationData {
|
location_data: info.location().map(|location| LocationData {
|
||||||
@@ -225,13 +225,13 @@ pub fn monitor_main_thread_hangs(
|
|||||||
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
|
||||||
|
use ::telemetry::{BacktraceFrame, HangReport};
|
||||||
use http_client::Method;
|
use http_client::Method;
|
||||||
use std::{
|
use std::{
|
||||||
ffi::c_int,
|
ffi::c_int,
|
||||||
sync::{mpsc, OnceLock},
|
sync::{mpsc, OnceLock},
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
use telemetry_events::{BacktraceFrame, HangReport};
|
|
||||||
|
|
||||||
use nix::sys::pthread;
|
use nix::sys::pthread;
|
||||||
|
|
||||||
@@ -490,7 +490,7 @@ async fn upload_previous_panics(
|
|||||||
async fn upload_panic(
|
async fn upload_panic(
|
||||||
http: &Arc<HttpClientWithUrl>,
|
http: &Arc<HttpClientWithUrl>,
|
||||||
panic_report_url: &Url,
|
panic_report_url: &Url,
|
||||||
panic: telemetry_events::Panic,
|
panic: ::telemetry::Panic,
|
||||||
most_recent_panic: &mut Option<(i64, String)>,
|
most_recent_panic: &mut Option<(i64, String)>,
|
||||||
) -> Result<bool> {
|
) -> Result<bool> {
|
||||||
*most_recent_panic = Some((panic.panicked_on, panic.payload.clone()));
|
*most_recent_panic = Some((panic.panicked_on, panic.payload.clone()));
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ Telemetry is sent from the application to our servers. Data is proxied through o
|
|||||||
|
|
||||||
Diagnostic events include debug information (stack traces) from crash reports. Reports are sent on the first application launch after the crash occurred. We've built dashboards that allow us to visualize the frequency and severity of issues experienced by users. Having these reports sent automatically allows us to begin implementing fixes without the user needing to file a report in our issue tracker. The plots in the dashboards also give us an informal measurement of the stability of Zed.
|
Diagnostic events include debug information (stack traces) from crash reports. Reports are sent on the first application launch after the crash occurred. We've built dashboards that allow us to visualize the frequency and severity of issues experienced by users. Having these reports sent automatically allows us to begin implementing fixes without the user needing to file a report in our issue tracker. The plots in the dashboards also give us an informal measurement of the stability of Zed.
|
||||||
|
|
||||||
You can see what data is sent when a panic occurs by inspecting the `Panic` struct in [crates/telemetry_events/src/telemetry_events.rs](https://github.com/zed-industries/zed/blob/main/crates/telemetry_events/src/telemetry_events.rs) in the Zed repo. You can find additional information in the [Debugging Crashes](./development/debugging-crashes.md) documentation.
|
You can see what data is sent when a panic occurs by inspecting the `Panic` struct in [crates/telemetry/src/telemetry.rs](https://github.com/zed-industries/zed/blob/main/crates/telemetry/src/telemetry.rs) in the Zed repo. You can find additional information in the [Debugging Crashes](./development/debugging-crashes.md) documentation.
|
||||||
|
|
||||||
### Usage Data (Metrics) {#metrics}
|
### Usage Data (Metrics) {#metrics}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user