Merge pull request #2020 from zed-industries/telemtry-opt-out
Telemetry opt out
This commit is contained in:
@@ -79,6 +79,13 @@
|
|||||||
"hard_tabs": false,
|
"hard_tabs": false,
|
||||||
// How many columns a tab should occupy.
|
// How many columns a tab should occupy.
|
||||||
"tab_size": 4,
|
"tab_size": 4,
|
||||||
|
// Control what info Zed sends to our servers
|
||||||
|
"telemetry": {
|
||||||
|
// Send debug info like crash reports.
|
||||||
|
"diagnostics": true,
|
||||||
|
// Send anonymized usage data like what languages you're using Zed with.
|
||||||
|
"metrics": true
|
||||||
|
},
|
||||||
// Git gutter behavior configuration.
|
// Git gutter behavior configuration.
|
||||||
"git": {
|
"git": {
|
||||||
// Control whether the git gutter is shown. May take 2 values:
|
// Control whether the git gutter is shown. May take 2 values:
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ use postage::watch;
|
|||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use rpc::proto::{AnyTypedEnvelope, EntityMessage, EnvelopedMessage, PeerId, RequestMessage};
|
use rpc::proto::{AnyTypedEnvelope, EntityMessage, EnvelopedMessage, PeerId, RequestMessage};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
use settings::{Settings, TelemetrySettings};
|
||||||
use std::{
|
use std::{
|
||||||
any::TypeId,
|
any::TypeId,
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
@@ -423,7 +424,9 @@ impl Client {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
Status::SignedOut | Status::UpgradeRequired => {
|
Status::SignedOut | Status::UpgradeRequired => {
|
||||||
self.telemetry.set_authenticated_user_info(None, false);
|
let telemetry_settings = cx.read(|cx| cx.global::<Settings>().telemetry());
|
||||||
|
self.telemetry
|
||||||
|
.set_authenticated_user_info(None, false, telemetry_settings);
|
||||||
state._reconnect_task.take();
|
state._reconnect_task.take();
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
@@ -706,7 +709,13 @@ impl Client {
|
|||||||
credentials = read_credentials_from_keychain(cx);
|
credentials = read_credentials_from_keychain(cx);
|
||||||
read_from_keychain = credentials.is_some();
|
read_from_keychain = credentials.is_some();
|
||||||
if read_from_keychain {
|
if read_from_keychain {
|
||||||
self.report_event("read credentials from keychain", Default::default());
|
cx.read(|cx| {
|
||||||
|
self.report_event(
|
||||||
|
"read credentials from keychain",
|
||||||
|
Default::default(),
|
||||||
|
cx.global::<Settings>().telemetry(),
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if credentials.is_none() {
|
if credentials.is_none() {
|
||||||
@@ -997,6 +1006,8 @@ impl Client {
|
|||||||
let executor = cx.background();
|
let executor = cx.background();
|
||||||
let telemetry = self.telemetry.clone();
|
let telemetry = self.telemetry.clone();
|
||||||
let http = self.http.clone();
|
let http = self.http.clone();
|
||||||
|
let metrics_enabled = cx.read(|cx| cx.global::<Settings>().telemetry());
|
||||||
|
|
||||||
executor.clone().spawn(async move {
|
executor.clone().spawn(async move {
|
||||||
// Generate a pair of asymmetric encryption keys. The public key will be used by the
|
// Generate a pair of asymmetric encryption keys. The public key will be used by the
|
||||||
// zed server to encrypt the user's access token, so that it can'be intercepted by
|
// zed server to encrypt the user's access token, so that it can'be intercepted by
|
||||||
@@ -1079,7 +1090,11 @@ impl Client {
|
|||||||
.context("failed to decrypt access token")?;
|
.context("failed to decrypt access token")?;
|
||||||
platform.activate(true);
|
platform.activate(true);
|
||||||
|
|
||||||
telemetry.report_event("authenticate with browser", Default::default());
|
telemetry.report_event(
|
||||||
|
"authenticate with browser",
|
||||||
|
Default::default(),
|
||||||
|
metrics_enabled,
|
||||||
|
);
|
||||||
|
|
||||||
Ok(Credentials {
|
Ok(Credentials {
|
||||||
user_id: user_id.parse()?,
|
user_id: user_id.parse()?,
|
||||||
@@ -1287,8 +1302,14 @@ impl Client {
|
|||||||
self.telemetry.start();
|
self.telemetry.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn report_event(&self, kind: &str, properties: Value) {
|
pub fn report_event(
|
||||||
self.telemetry.report_event(kind, properties.clone());
|
&self,
|
||||||
|
kind: &str,
|
||||||
|
properties: Value,
|
||||||
|
telemetry_settings: TelemetrySettings,
|
||||||
|
) {
|
||||||
|
self.telemetry
|
||||||
|
.report_event(kind, properties.clone(), telemetry_settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn telemetry_log_file_path(&self) -> Option<PathBuf> {
|
pub fn telemetry_log_file_path(&self) -> Option<PathBuf> {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use lazy_static::lazy_static;
|
|||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
use settings::TelemetrySettings;
|
||||||
use std::{
|
use std::{
|
||||||
io::Write,
|
io::Write,
|
||||||
mem,
|
mem,
|
||||||
@@ -184,11 +185,18 @@ impl Telemetry {
|
|||||||
.detach();
|
.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This method takes the entire TelemetrySettings struct in order to force client code
|
||||||
|
/// to pull the struct out of the settings global. Do not remove!
|
||||||
pub fn set_authenticated_user_info(
|
pub fn set_authenticated_user_info(
|
||||||
self: &Arc<Self>,
|
self: &Arc<Self>,
|
||||||
metrics_id: Option<String>,
|
metrics_id: Option<String>,
|
||||||
is_staff: bool,
|
is_staff: bool,
|
||||||
|
telemetry_settings: TelemetrySettings,
|
||||||
) {
|
) {
|
||||||
|
if !telemetry_settings.metrics() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let this = self.clone();
|
let this = self.clone();
|
||||||
let mut state = self.state.lock();
|
let mut state = self.state.lock();
|
||||||
let device_id = state.device_id.clone();
|
let device_id = state.device_id.clone();
|
||||||
@@ -221,7 +229,16 @@ impl Telemetry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn report_event(self: &Arc<Self>, kind: &str, properties: Value) {
|
pub fn report_event(
|
||||||
|
self: &Arc<Self>,
|
||||||
|
kind: &str,
|
||||||
|
properties: Value,
|
||||||
|
telemetry_settings: TelemetrySettings,
|
||||||
|
) {
|
||||||
|
if !telemetry_settings.metrics() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let mut state = self.state.lock();
|
let mut state = self.state.lock();
|
||||||
let event = MixpanelEvent {
|
let event = MixpanelEvent {
|
||||||
event: kind.to_string(),
|
event: kind.to_string(),
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use futures::{channel::mpsc, future, AsyncReadExt, Future, StreamExt};
|
|||||||
use gpui::{AsyncAppContext, Entity, ImageData, ModelContext, ModelHandle, Task};
|
use gpui::{AsyncAppContext, Entity, ImageData, ModelContext, ModelHandle, Task};
|
||||||
use postage::{sink::Sink, watch};
|
use postage::{sink::Sink, watch};
|
||||||
use rpc::proto::{RequestMessage, UsersResponse};
|
use rpc::proto::{RequestMessage, UsersResponse};
|
||||||
|
use settings::Settings;
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
use util::TryFutureExt as _;
|
use util::TryFutureExt as _;
|
||||||
|
|
||||||
@@ -141,14 +142,11 @@ impl UserStore {
|
|||||||
let fetch_metrics_id =
|
let fetch_metrics_id =
|
||||||
client.request(proto::GetPrivateUserInfo {}).log_err();
|
client.request(proto::GetPrivateUserInfo {}).log_err();
|
||||||
let (user, info) = futures::join!(fetch_user, fetch_metrics_id);
|
let (user, info) = futures::join!(fetch_user, fetch_metrics_id);
|
||||||
if let Some(info) = info {
|
client.telemetry.set_authenticated_user_info(
|
||||||
client.telemetry.set_authenticated_user_info(
|
info.as_ref().map(|info| info.metrics_id.clone()),
|
||||||
Some(info.metrics_id.clone()),
|
info.as_ref().map(|info| info.staff).unwrap_or(false),
|
||||||
info.staff,
|
cx.read(|cx| cx.global::<Settings>().telemetry()),
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
client.telemetry.set_authenticated_user_info(None, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
current_user_tx.send(user).await.ok();
|
current_user_tx.send(user).await.ok();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ use rand::{
|
|||||||
distributions::{Alphanumeric, DistString},
|
distributions::{Alphanumeric, DistString},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
|
use settings::Settings;
|
||||||
use std::{env, ffi::OsStr, path::PathBuf, sync::Arc};
|
use std::{env, ffi::OsStr, path::PathBuf, sync::Arc};
|
||||||
|
|
||||||
#[gpui::test(iterations = 100)]
|
#[gpui::test(iterations = 100)]
|
||||||
@@ -104,6 +105,8 @@ async fn test_random_collaboration(
|
|||||||
cx.function_name.clone(),
|
cx.function_name.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
client_cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||||
|
|
||||||
let op_start_signal = futures::channel::mpsc::unbounded();
|
let op_start_signal = futures::channel::mpsc::unbounded();
|
||||||
let client = server.create_client(&mut client_cx, &username).await;
|
let client = server.create_client(&mut client_cx, &username).await;
|
||||||
user_ids.push(client.current_user_id(&client_cx));
|
user_ids.push(client.current_user_id(&client_cx));
|
||||||
@@ -173,6 +176,7 @@ async fn test_random_collaboration(
|
|||||||
available_users.push((removed_user_id, client.username.clone()));
|
available_users.push((removed_user_id, client.username.clone()));
|
||||||
client_cx.update(|cx| {
|
client_cx.update(|cx| {
|
||||||
cx.clear_globals();
|
cx.clear_globals();
|
||||||
|
cx.set_global(Settings::test(cx));
|
||||||
drop(client);
|
drop(client);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -401,6 +405,7 @@ async fn test_random_collaboration(
|
|||||||
for (client, mut cx) in clients {
|
for (client, mut cx) in clients {
|
||||||
cx.update(|cx| {
|
cx.update(|cx| {
|
||||||
cx.clear_globals();
|
cx.clear_globals();
|
||||||
|
cx.set_global(Settings::test(cx));
|
||||||
drop(client);
|
drop(client);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6087,10 +6087,11 @@ impl Editor {
|
|||||||
let extension = Path::new(file.file_name(cx))
|
let extension = Path::new(file.file_name(cx))
|
||||||
.extension()
|
.extension()
|
||||||
.and_then(|e| e.to_str());
|
.and_then(|e| e.to_str());
|
||||||
project
|
project.read(cx).client().report_event(
|
||||||
.read(cx)
|
name,
|
||||||
.client()
|
json!({ "File Extension": extension }),
|
||||||
.report_event(name, json!({ "File Extension": extension }));
|
cx.global::<Settings>().telemetry(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,9 +51,26 @@ pub struct Settings {
|
|||||||
pub language_overrides: HashMap<Arc<str>, EditorSettings>,
|
pub language_overrides: HashMap<Arc<str>, EditorSettings>,
|
||||||
pub lsp: HashMap<Arc<str>, LspSettings>,
|
pub lsp: HashMap<Arc<str>, LspSettings>,
|
||||||
pub theme: Arc<Theme>,
|
pub theme: Arc<Theme>,
|
||||||
|
pub telemetry_defaults: TelemetrySettings,
|
||||||
|
pub telemetry_overrides: TelemetrySettings,
|
||||||
pub staff_mode: bool,
|
pub staff_mode: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
|
||||||
|
pub struct TelemetrySettings {
|
||||||
|
diagnostics: Option<bool>,
|
||||||
|
metrics: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TelemetrySettings {
|
||||||
|
pub fn metrics(&self) -> bool {
|
||||||
|
self.metrics.unwrap()
|
||||||
|
}
|
||||||
|
pub fn diagnostics(&self) -> bool {
|
||||||
|
self.diagnostics.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
|
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
|
||||||
pub struct FeatureFlags {
|
pub struct FeatureFlags {
|
||||||
pub experimental_themes: bool,
|
pub experimental_themes: bool,
|
||||||
@@ -302,6 +319,8 @@ pub struct SettingsFileContent {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub theme: Option<String>,
|
pub theme: Option<String>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
pub telemetry: TelemetrySettings,
|
||||||
|
#[serde(default)]
|
||||||
pub staff_mode: Option<bool>,
|
pub staff_mode: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -312,6 +331,7 @@ pub struct LspSettings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Settings {
|
impl Settings {
|
||||||
|
/// Fill out the settings corresponding to the default.json file, overrides will be set later
|
||||||
pub fn defaults(
|
pub fn defaults(
|
||||||
assets: impl AssetSource,
|
assets: impl AssetSource,
|
||||||
font_cache: &FontCache,
|
font_cache: &FontCache,
|
||||||
@@ -363,11 +383,13 @@ impl Settings {
|
|||||||
language_overrides: Default::default(),
|
language_overrides: Default::default(),
|
||||||
lsp: defaults.lsp.clone(),
|
lsp: defaults.lsp.clone(),
|
||||||
theme: themes.get(&defaults.theme.unwrap()).unwrap(),
|
theme: themes.get(&defaults.theme.unwrap()).unwrap(),
|
||||||
|
telemetry_defaults: defaults.telemetry,
|
||||||
|
telemetry_overrides: Default::default(),
|
||||||
staff_mode: false,
|
staff_mode: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fill out the overrride and etc. settings from the user's settings.json
|
||||||
pub fn set_user_settings(
|
pub fn set_user_settings(
|
||||||
&mut self,
|
&mut self,
|
||||||
data: SettingsFileContent,
|
data: SettingsFileContent,
|
||||||
@@ -419,6 +441,7 @@ impl Settings {
|
|||||||
self.terminal_overrides.copy_on_select = data.terminal.copy_on_select;
|
self.terminal_overrides.copy_on_select = data.terminal.copy_on_select;
|
||||||
self.terminal_overrides = data.terminal;
|
self.terminal_overrides = data.terminal;
|
||||||
self.language_overrides = data.languages;
|
self.language_overrides = data.languages;
|
||||||
|
self.telemetry_overrides = data.telemetry;
|
||||||
self.lsp = data.lsp;
|
self.lsp = data.lsp;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -489,6 +512,27 @@ impl Settings {
|
|||||||
.unwrap_or_else(|| R::default())
|
.unwrap_or_else(|| R::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn telemetry(&self) -> TelemetrySettings {
|
||||||
|
TelemetrySettings {
|
||||||
|
diagnostics: Some(self.telemetry_diagnostics()),
|
||||||
|
metrics: Some(self.telemetry_metrics()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn telemetry_diagnostics(&self) -> bool {
|
||||||
|
self.telemetry_overrides
|
||||||
|
.diagnostics
|
||||||
|
.or(self.telemetry_defaults.diagnostics)
|
||||||
|
.expect("missing default")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn telemetry_metrics(&self) -> bool {
|
||||||
|
self.telemetry_overrides
|
||||||
|
.metrics
|
||||||
|
.or(self.telemetry_defaults.metrics)
|
||||||
|
.expect("missing default")
|
||||||
|
}
|
||||||
|
|
||||||
pub fn terminal_scroll(&self) -> AlternateScroll {
|
pub fn terminal_scroll(&self) -> AlternateScroll {
|
||||||
self.terminal_setting(|terminal_setting| terminal_setting.alternate_scroll.as_ref())
|
self.terminal_setting(|terminal_setting| terminal_setting.alternate_scroll.as_ref())
|
||||||
}
|
}
|
||||||
@@ -540,6 +584,11 @@ impl Settings {
|
|||||||
lsp: Default::default(),
|
lsp: Default::default(),
|
||||||
projects_online_by_default: true,
|
projects_online_by_default: true,
|
||||||
theme: gpui::fonts::with_font_cache(cx.font_cache().clone(), Default::default),
|
theme: gpui::fonts::with_font_cache(cx.font_cache().clone(), Default::default),
|
||||||
|
telemetry_defaults: TelemetrySettings {
|
||||||
|
diagnostics: Some(true),
|
||||||
|
metrics: Some(true),
|
||||||
|
},
|
||||||
|
telemetry_overrides: Default::default(),
|
||||||
staff_mode: false,
|
staff_mode: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -470,7 +470,7 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
dock,
|
dock,
|
||||||
item::test::TestItem,
|
item::{self, test::TestItem},
|
||||||
persistence::model::{
|
persistence::model::{
|
||||||
SerializedItem, SerializedPane, SerializedPaneGroup, SerializedWorkspace,
|
SerializedItem, SerializedPane, SerializedPaneGroup, SerializedWorkspace,
|
||||||
},
|
},
|
||||||
@@ -492,7 +492,7 @@ mod tests {
|
|||||||
Settings::test_async(cx);
|
Settings::test_async(cx);
|
||||||
|
|
||||||
cx.update(|cx| {
|
cx.update(|cx| {
|
||||||
register_deserializable_item::<TestItem>(cx);
|
register_deserializable_item::<item::test::TestItem>(cx);
|
||||||
});
|
});
|
||||||
|
|
||||||
let serialized_workspace = SerializedWorkspace {
|
let serialized_workspace = SerializedWorkspace {
|
||||||
@@ -508,7 +508,7 @@ mod tests {
|
|||||||
children: vec![SerializedItem {
|
children: vec![SerializedItem {
|
||||||
active: true,
|
active: true,
|
||||||
item_id: 0,
|
item_id: 0,
|
||||||
kind: "test".into(),
|
kind: "TestItem".into(),
|
||||||
}],
|
}],
|
||||||
},
|
},
|
||||||
left_sidebar_open: false,
|
left_sidebar_open: false,
|
||||||
|
|||||||
@@ -919,7 +919,7 @@ pub(crate) mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn serialized_item_kind() -> Option<&'static str> {
|
fn serialized_item_kind() -> Option<&'static str> {
|
||||||
None
|
Some("TestItem")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize(
|
fn deserialize(
|
||||||
|
|||||||
+86
-72
@@ -18,7 +18,7 @@ use futures::{
|
|||||||
channel::{mpsc, oneshot},
|
channel::{mpsc, oneshot},
|
||||||
FutureExt, SinkExt, StreamExt,
|
FutureExt, SinkExt, StreamExt,
|
||||||
};
|
};
|
||||||
use gpui::{executor::Background, App, AssetSource, AsyncAppContext, Task, ViewContext};
|
use gpui::{App, AssetSource, AsyncAppContext, MutableAppContext, Task, ViewContext};
|
||||||
use isahc::{config::Configurable, Request};
|
use isahc::{config::Configurable, Request};
|
||||||
use language::LanguageRegistry;
|
use language::LanguageRegistry;
|
||||||
use log::LevelFilter;
|
use log::LevelFilter;
|
||||||
@@ -50,10 +50,13 @@ fn main() {
|
|||||||
|
|
||||||
log::info!("========== starting zed ==========");
|
log::info!("========== starting zed ==========");
|
||||||
let mut app = gpui::App::new(Assets).unwrap();
|
let mut app = gpui::App::new(Assets).unwrap();
|
||||||
|
|
||||||
let app_version = ZED_APP_VERSION
|
let app_version = ZED_APP_VERSION
|
||||||
.or_else(|| app.platform().app_version().ok())
|
.or_else(|| app.platform().app_version().ok())
|
||||||
.map_or("dev".to_string(), |v| v.to_string());
|
.map_or("dev".to_string(), |v| v.to_string());
|
||||||
init_panic_hook(app_version, http.clone(), app.background());
|
init_panic_hook(app_version);
|
||||||
|
|
||||||
|
app.background();
|
||||||
|
|
||||||
load_embedded_fonts(&app);
|
load_embedded_fonts(&app);
|
||||||
|
|
||||||
@@ -61,7 +64,6 @@ fn main() {
|
|||||||
|
|
||||||
let themes = ThemeRegistry::new(Assets, app.font_cache());
|
let themes = ThemeRegistry::new(Assets, app.font_cache());
|
||||||
let default_settings = Settings::defaults(Assets, &app.font_cache(), &themes);
|
let default_settings = Settings::defaults(Assets, &app.font_cache(), &themes);
|
||||||
|
|
||||||
let config_files = load_config_files(&app, fs.clone());
|
let config_files = load_config_files(&app, fs.clone());
|
||||||
|
|
||||||
let login_shell_env_loaded = if stdout_is_a_pty() {
|
let login_shell_env_loaded = if stdout_is_a_pty() {
|
||||||
@@ -88,6 +90,18 @@ fn main() {
|
|||||||
cx.set_global(*RELEASE_CHANNEL);
|
cx.set_global(*RELEASE_CHANNEL);
|
||||||
cx.set_global(HomeDir(paths::HOME.to_path_buf()));
|
cx.set_global(HomeDir(paths::HOME.to_path_buf()));
|
||||||
|
|
||||||
|
let (settings_file_content, keymap_file) = cx.background().block(config_files).unwrap();
|
||||||
|
|
||||||
|
//Setup settings global before binding actions
|
||||||
|
cx.set_global(SettingsFile::new(
|
||||||
|
&paths::SETTINGS,
|
||||||
|
settings_file_content.clone(),
|
||||||
|
fs.clone(),
|
||||||
|
));
|
||||||
|
|
||||||
|
watch_settings_file(default_settings, settings_file_content, themes.clone(), cx);
|
||||||
|
upload_previous_panics(http.clone(), cx);
|
||||||
|
|
||||||
let client = client::Client::new(http.clone(), cx);
|
let client = client::Client::new(http.clone(), cx);
|
||||||
let mut languages = LanguageRegistry::new(login_shell_env_loaded);
|
let mut languages = LanguageRegistry::new(login_shell_env_loaded);
|
||||||
languages.set_language_server_download_dir(paths::LANGUAGES_DIR.clone());
|
languages.set_language_server_download_dir(paths::LANGUAGES_DIR.clone());
|
||||||
@@ -97,15 +111,6 @@ fn main() {
|
|||||||
.spawn(languages::init(languages.clone(), cx.background().clone()));
|
.spawn(languages::init(languages.clone(), cx.background().clone()));
|
||||||
let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http.clone(), cx));
|
let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http.clone(), cx));
|
||||||
|
|
||||||
let (settings_file_content, keymap_file) = cx.background().block(config_files).unwrap();
|
|
||||||
|
|
||||||
//Setup settings global before binding actions
|
|
||||||
cx.set_global(SettingsFile::new(
|
|
||||||
&paths::SETTINGS,
|
|
||||||
settings_file_content.clone(),
|
|
||||||
fs.clone(),
|
|
||||||
));
|
|
||||||
watch_settings_file(default_settings, settings_file_content, themes.clone(), cx);
|
|
||||||
watch_keymap_file(keymap_file, cx);
|
watch_keymap_file(keymap_file, cx);
|
||||||
|
|
||||||
context_menu::init(cx);
|
context_menu::init(cx);
|
||||||
@@ -143,7 +148,11 @@ fn main() {
|
|||||||
.detach();
|
.detach();
|
||||||
|
|
||||||
client.start_telemetry();
|
client.start_telemetry();
|
||||||
client.report_event("start app", Default::default());
|
client.report_event(
|
||||||
|
"start app",
|
||||||
|
Default::default(),
|
||||||
|
cx.global::<Settings>().telemetry(),
|
||||||
|
);
|
||||||
|
|
||||||
let app_state = Arc::new(AppState {
|
let app_state = Arc::new(AppState {
|
||||||
languages,
|
languages,
|
||||||
@@ -251,65 +260,7 @@ fn init_logger() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_panic_hook(app_version: String, http: Arc<dyn HttpClient>, background: Arc<Background>) {
|
fn init_panic_hook(app_version: String) {
|
||||||
background
|
|
||||||
.spawn({
|
|
||||||
async move {
|
|
||||||
let panic_report_url = format!("{}/api/panic", &*client::ZED_SERVER_URL);
|
|
||||||
let mut children = smol::fs::read_dir(&*paths::LOGS_DIR).await?;
|
|
||||||
while let Some(child) = children.next().await {
|
|
||||||
let child = child?;
|
|
||||||
let child_path = child.path();
|
|
||||||
if child_path.extension() != Some(OsStr::new("panic")) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let filename = if let Some(filename) = child_path.file_name() {
|
|
||||||
filename.to_string_lossy()
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut components = filename.split('-');
|
|
||||||
if components.next() != Some("zed") {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let version = if let Some(version) = components.next() {
|
|
||||||
version
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
let text = smol::fs::read_to_string(&child_path)
|
|
||||||
.await
|
|
||||||
.context("error reading panic file")?;
|
|
||||||
let body = serde_json::to_string(&json!({
|
|
||||||
"text": text,
|
|
||||||
"version": version,
|
|
||||||
"token": ZED_SECRET_CLIENT_TOKEN,
|
|
||||||
}))
|
|
||||||
.unwrap();
|
|
||||||
let request = Request::post(&panic_report_url)
|
|
||||||
.redirect_policy(isahc::config::RedirectPolicy::Follow)
|
|
||||||
.header("Content-Type", "application/json")
|
|
||||||
.body(body.into())?;
|
|
||||||
let response = http.send(request).await.context("error sending panic")?;
|
|
||||||
if response.status().is_success() {
|
|
||||||
std::fs::remove_file(child_path)
|
|
||||||
.context("error removing panic after sending it successfully")
|
|
||||||
.log_err();
|
|
||||||
} else {
|
|
||||||
return Err(anyhow!(
|
|
||||||
"error uploading panic to server: {}",
|
|
||||||
response.status()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok::<_, anyhow::Error>(())
|
|
||||||
}
|
|
||||||
.log_err()
|
|
||||||
})
|
|
||||||
.detach();
|
|
||||||
|
|
||||||
let is_pty = stdout_is_a_pty();
|
let is_pty = stdout_is_a_pty();
|
||||||
panic::set_hook(Box::new(move |info| {
|
panic::set_hook(Box::new(move |info| {
|
||||||
let backtrace = Backtrace::new();
|
let backtrace = Backtrace::new();
|
||||||
@@ -358,6 +309,69 @@ fn init_panic_hook(app_version: String, http: Arc<dyn HttpClient>, background: A
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn upload_previous_panics(http: Arc<dyn HttpClient>, cx: &mut MutableAppContext) {
|
||||||
|
let diagnostics_telemetry = cx.global::<Settings>().telemetry_diagnostics();
|
||||||
|
|
||||||
|
cx.background()
|
||||||
|
.spawn({
|
||||||
|
async move {
|
||||||
|
let panic_report_url = format!("{}/api/panic", &*client::ZED_SERVER_URL);
|
||||||
|
let mut children = smol::fs::read_dir(&*paths::LOGS_DIR).await?;
|
||||||
|
while let Some(child) = children.next().await {
|
||||||
|
let child = child?;
|
||||||
|
let child_path = child.path();
|
||||||
|
|
||||||
|
if child_path.extension() != Some(OsStr::new("panic")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let filename = if let Some(filename) = child_path.file_name() {
|
||||||
|
filename.to_string_lossy()
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut components = filename.split('-');
|
||||||
|
if components.next() != Some("zed") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let version = if let Some(version) = components.next() {
|
||||||
|
version
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
if diagnostics_telemetry {
|
||||||
|
let text = smol::fs::read_to_string(&child_path)
|
||||||
|
.await
|
||||||
|
.context("error reading panic file")?;
|
||||||
|
let body = serde_json::to_string(&json!({
|
||||||
|
"text": text,
|
||||||
|
"version": version,
|
||||||
|
"token": ZED_SECRET_CLIENT_TOKEN,
|
||||||
|
}))
|
||||||
|
.unwrap();
|
||||||
|
let request = Request::post(&panic_report_url)
|
||||||
|
.redirect_policy(isahc::config::RedirectPolicy::Follow)
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.body(body.into())?;
|
||||||
|
let response = http.send(request).await.context("error sending panic")?;
|
||||||
|
if !response.status().is_success() {
|
||||||
|
log::error!("Error uploading panic to server: {}", response.status());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We've done what we can, delete the file
|
||||||
|
std::fs::remove_file(child_path)
|
||||||
|
.context("error removing panic")
|
||||||
|
.log_err();
|
||||||
|
}
|
||||||
|
Ok::<_, anyhow::Error>(())
|
||||||
|
}
|
||||||
|
.log_err()
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
}
|
||||||
|
|
||||||
async fn load_login_shell_environment() -> Result<()> {
|
async fn load_login_shell_environment() -> Result<()> {
|
||||||
let marker = "ZED_LOGIN_SHELL_START";
|
let marker = "ZED_LOGIN_SHELL_START";
|
||||||
let shell = env::var("SHELL").context(
|
let shell = env::var("SHELL").context(
|
||||||
|
|||||||
Reference in New Issue
Block a user