telemetry_events.rs

  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}