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