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