1//! See [Telemetry in Zed](https://zed.dev/docs/telemetry) for additional information.
2
3use language::LanguageName;
4use semantic_version::SemanticVersion;
5use serde::{Deserialize, Serialize};
6use std::{fmt::Display, sync::Arc, time::Duration};
7
8#[derive(Serialize, Deserialize, Debug)]
9pub struct EventRequestBody {
10 /// Identifier unique to each system Zed is installed on
11 pub system_id: Option<String>,
12 /// Identifier unique to each Zed installation (differs for stable, preview, dev)
13 pub installation_id: Option<String>,
14 /// Identifier unique to each logged in Zed user (randomly generated on first sign in)
15 /// Identifier unique to each Zed session (differs for each time you open Zed)
16 pub session_id: Option<String>,
17 pub metrics_id: Option<String>,
18 /// True for Zed staff, otherwise false
19 pub is_staff: Option<bool>,
20 /// Zed version number
21 pub app_version: String,
22 pub os_name: String,
23 pub os_version: Option<String>,
24 pub architecture: String,
25 /// Zed release channel (stable, preview, dev)
26 pub release_channel: Option<String>,
27 pub events: Vec<EventWrapper>,
28}
29
30impl EventRequestBody {
31 pub fn semver(&self) -> Option<SemanticVersion> {
32 self.app_version.parse().ok()
33 }
34}
35
36#[derive(Serialize, Deserialize, Debug)]
37pub struct EventWrapper {
38 pub signed_in: bool,
39 /// Duration between this event's timestamp and the timestamp of the first event in the current batch
40 pub milliseconds_since_first_event: i64,
41 /// The event itself
42 #[serde(flatten)]
43 pub event: Event,
44}
45
46#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
47#[serde(rename_all = "snake_case")]
48pub enum AssistantKind {
49 Panel,
50 Inline,
51}
52impl Display for AssistantKind {
53 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54 write!(
55 f,
56 "{}",
57 match self {
58 Self::Panel => "panel",
59 Self::Inline => "inline",
60 }
61 )
62 }
63}
64
65#[derive(Default, Clone, Debug, PartialEq, Serialize, Deserialize)]
66#[serde(rename_all = "snake_case")]
67pub enum AssistantPhase {
68 #[default]
69 Response,
70 Invoked,
71 Accepted,
72 Rejected,
73}
74
75impl Display for AssistantPhase {
76 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
77 write!(
78 f,
79 "{}",
80 match self {
81 Self::Response => "response",
82 Self::Invoked => "invoked",
83 Self::Accepted => "accepted",
84 Self::Rejected => "rejected",
85 }
86 )
87 }
88}
89
90#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
91#[serde(tag = "type")]
92pub enum Event {
93 Editor(EditorEvent),
94 InlineCompletion(InlineCompletionEvent),
95 Call(CallEvent),
96 Assistant(AssistantEvent),
97 Cpu(CpuEvent),
98 Memory(MemoryEvent),
99 App(AppEvent),
100 Setting(SettingEvent),
101 Extension(ExtensionEvent),
102 Edit(EditEvent),
103 Action(ActionEvent),
104 Repl(ReplEvent),
105}
106
107#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
108pub struct EditorEvent {
109 /// The editor operation performed (open, save)
110 pub operation: String,
111 /// The extension of the file that was opened or saved
112 pub file_extension: Option<String>,
113 /// Whether the user is in vim mode or not
114 pub vim_mode: bool,
115 /// Whether the user has copilot enabled or not
116 pub copilot_enabled: bool,
117 /// Whether the user has copilot enabled for the language of the file opened or saved
118 pub copilot_enabled_for_language: bool,
119 /// Whether the client is opening/saving a local file or a remote file via SSH
120 #[serde(default)]
121 pub is_via_ssh: bool,
122}
123
124#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
125pub struct InlineCompletionEvent {
126 /// Provider of the completion suggestion (e.g. copilot, supermaven)
127 pub provider: String,
128 pub suggestion_accepted: bool,
129 pub file_extension: Option<String>,
130}
131
132#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
133pub struct CallEvent {
134 /// Operation performed: invite/join call; begin/end screenshare; share/unshare project; etc
135 pub operation: String,
136 pub room_id: Option<u64>,
137 pub channel_id: Option<u64>,
138}
139
140#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
141pub struct AssistantEvent {
142 /// Unique random identifier for each assistant tab (None for inline assist)
143 pub conversation_id: Option<String>,
144 /// The kind of assistant (Panel, Inline)
145 pub kind: AssistantKind,
146 #[serde(default)]
147 pub phase: AssistantPhase,
148 /// Name of the AI model used (gpt-4o, claude-3-5-sonnet, etc)
149 pub model: String,
150 pub model_provider: String,
151 pub response_latency: Option<Duration>,
152 pub error_message: Option<String>,
153 pub language_name: Option<LanguageName>,
154}
155
156#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
157pub struct CpuEvent {
158 pub usage_as_percentage: f32,
159 pub core_count: u32,
160}
161
162#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
163pub struct MemoryEvent {
164 pub memory_in_bytes: u64,
165 pub virtual_memory_in_bytes: u64,
166}
167
168#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
169pub struct ActionEvent {
170 pub source: String,
171 pub action: String,
172}
173
174#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
175pub struct EditEvent {
176 pub duration: i64,
177 pub environment: String,
178 /// Whether the edits occurred locally or remotely via SSH
179 #[serde(default)]
180 pub is_via_ssh: bool,
181}
182
183#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
184pub struct SettingEvent {
185 pub setting: String,
186 pub value: String,
187}
188
189#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
190pub struct ExtensionEvent {
191 pub extension_id: Arc<str>,
192 pub version: Arc<str>,
193}
194
195#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
196pub struct AppEvent {
197 pub operation: String,
198}
199
200#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
201pub struct ReplEvent {
202 pub kernel_language: String,
203 pub kernel_status: String,
204 pub repl_session_id: String,
205}
206
207#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
208pub struct BacktraceFrame {
209 pub ip: usize,
210 pub symbol_addr: usize,
211 pub base: Option<usize>,
212 pub symbols: Vec<String>,
213}
214
215#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
216pub struct HangReport {
217 pub backtrace: Vec<BacktraceFrame>,
218 pub app_version: Option<SemanticVersion>,
219 pub os_name: String,
220 pub os_version: Option<String>,
221 pub architecture: String,
222 /// Identifier unique to each Zed installation (differs for stable, preview, dev)
223 pub installation_id: Option<String>,
224}
225
226#[derive(Serialize, Deserialize)]
227pub struct LocationData {
228 pub file: String,
229 pub line: u32,
230}
231
232#[derive(Serialize, Deserialize)]
233pub struct Panic {
234 /// The name of the thread that panicked
235 pub thread: String,
236 /// The panic message
237 pub payload: String,
238 /// The location of the panic (file, line number)
239 #[serde(skip_serializing_if = "Option::is_none")]
240 pub location_data: Option<LocationData>,
241 pub backtrace: Vec<String>,
242 /// Zed version number
243 pub app_version: String,
244 /// Zed release channel (stable, preview, dev)
245 pub release_channel: String,
246 pub os_name: String,
247 pub os_version: Option<String>,
248 pub architecture: String,
249 /// The time the panic occurred (UNIX millisecond timestamp)
250 pub panicked_on: i64,
251 /// Identifier unique to each system Zed is installed on
252 #[serde(skip_serializing_if = "Option::is_none")]
253 pub system_id: Option<String>,
254 /// Identifier unique to each Zed installation (differs for stable, preview, dev)
255 #[serde(skip_serializing_if = "Option::is_none")]
256 pub installation_id: Option<String>,
257 /// Identifier unique to each Zed session (differs for each time you open Zed)
258 pub session_id: String,
259}
260
261#[derive(Serialize, Deserialize)]
262pub struct PanicRequest {
263 pub panic: Panic,
264}