Add system_id (#18040)
This PR adds `system_id` to telemetry, which is contained within a new `global` database (accessible by any release channel of Zed on a single system). This will help us get a more accurate understanding of user count, instead of relying on `installationd_id`, which is different per release channel. This doesn't solve the problem of a user with multiple machines, but it gets us closer. Release Notes: - N/A
This commit is contained in:
parent
5e6d1814e5
commit
ca4980df02
8 changed files with 184 additions and 62 deletions
|
@ -37,9 +37,10 @@ pub struct Telemetry {
|
|||
|
||||
struct TelemetryState {
|
||||
settings: TelemetrySettings,
|
||||
metrics_id: Option<Arc<str>>, // Per logged-in user
|
||||
system_id: Option<Arc<str>>, // Per system
|
||||
installation_id: Option<Arc<str>>, // Per app installation (different for dev, nightly, preview, and stable)
|
||||
session_id: Option<String>, // Per app launch
|
||||
metrics_id: Option<Arc<str>>, // Per logged-in user
|
||||
release_channel: Option<&'static str>,
|
||||
architecture: &'static str,
|
||||
events_queue: Vec<EventWrapper>,
|
||||
|
@ -191,9 +192,10 @@ impl Telemetry {
|
|||
settings: *TelemetrySettings::get_global(cx),
|
||||
architecture: env::consts::ARCH,
|
||||
release_channel,
|
||||
system_id: None,
|
||||
installation_id: None,
|
||||
metrics_id: None,
|
||||
session_id: None,
|
||||
metrics_id: None,
|
||||
events_queue: Vec::new(),
|
||||
flush_events_task: None,
|
||||
log_file: None,
|
||||
|
@ -283,11 +285,13 @@ impl Telemetry {
|
|||
|
||||
pub fn start(
|
||||
self: &Arc<Self>,
|
||||
system_id: Option<String>,
|
||||
installation_id: Option<String>,
|
||||
session_id: String,
|
||||
cx: &mut AppContext,
|
||||
) {
|
||||
let mut state = self.state.lock();
|
||||
state.system_id = system_id.map(|id| id.into());
|
||||
state.installation_id = installation_id.map(|id| id.into());
|
||||
state.session_id = Some(session_id);
|
||||
state.app_version = release_channel::AppVersion::global(cx).to_string();
|
||||
|
@ -637,9 +641,10 @@ impl Telemetry {
|
|||
let state = this.state.lock();
|
||||
|
||||
let request_body = EventRequestBody {
|
||||
system_id: state.system_id.as_deref().map(Into::into),
|
||||
installation_id: state.installation_id.as_deref().map(Into::into),
|
||||
metrics_id: state.metrics_id.as_deref().map(Into::into),
|
||||
session_id: state.session_id.clone(),
|
||||
metrics_id: state.metrics_id.as_deref().map(Into::into),
|
||||
is_staff: state.is_staff,
|
||||
app_version: state.app_version.clone(),
|
||||
os_name: state.os_name.clone(),
|
||||
|
@ -711,6 +716,7 @@ mod tests {
|
|||
Utc.with_ymd_and_hms(1990, 4, 12, 12, 0, 0).unwrap(),
|
||||
));
|
||||
let http = FakeHttpClient::with_200_response();
|
||||
let system_id = Some("system_id".to_string());
|
||||
let installation_id = Some("installation_id".to_string());
|
||||
let session_id = "session_id".to_string();
|
||||
|
||||
|
@ -718,7 +724,7 @@ mod tests {
|
|||
let telemetry = Telemetry::new(clock.clone(), http, cx);
|
||||
|
||||
telemetry.state.lock().max_queue_size = 4;
|
||||
telemetry.start(installation_id, session_id, cx);
|
||||
telemetry.start(system_id, installation_id, session_id, cx);
|
||||
|
||||
assert!(is_empty_state(&telemetry));
|
||||
|
||||
|
@ -796,13 +802,14 @@ mod tests {
|
|||
Utc.with_ymd_and_hms(1990, 4, 12, 12, 0, 0).unwrap(),
|
||||
));
|
||||
let http = FakeHttpClient::with_200_response();
|
||||
let system_id = Some("system_id".to_string());
|
||||
let installation_id = Some("installation_id".to_string());
|
||||
let session_id = "session_id".to_string();
|
||||
|
||||
cx.update(|cx| {
|
||||
let telemetry = Telemetry::new(clock.clone(), http, cx);
|
||||
telemetry.state.lock().max_queue_size = 4;
|
||||
telemetry.start(installation_id, session_id, cx);
|
||||
telemetry.start(system_id, installation_id, session_id, cx);
|
||||
|
||||
assert!(is_empty_state(&telemetry));
|
||||
|
||||
|
|
|
@ -149,7 +149,8 @@ pub async fn post_crash(
|
|||
installation_id = %installation_id,
|
||||
description = %description,
|
||||
backtrace = %summary,
|
||||
"crash report");
|
||||
"crash report"
|
||||
);
|
||||
|
||||
if let Some(slack_panics_webhook) = app.config.slack_panics_webhook.clone() {
|
||||
let payload = slack::WebhookBody::new(|w| {
|
||||
|
@ -627,7 +628,9 @@ where
|
|||
|
||||
#[derive(Serialize, Debug, clickhouse::Row)]
|
||||
pub struct EditorEventRow {
|
||||
system_id: String,
|
||||
installation_id: String,
|
||||
session_id: Option<String>,
|
||||
metrics_id: String,
|
||||
operation: String,
|
||||
app_version: String,
|
||||
|
@ -647,7 +650,6 @@ pub struct EditorEventRow {
|
|||
historical_event: bool,
|
||||
architecture: String,
|
||||
is_staff: Option<bool>,
|
||||
session_id: Option<String>,
|
||||
major: Option<i32>,
|
||||
minor: Option<i32>,
|
||||
patch: Option<i32>,
|
||||
|
@ -677,9 +679,10 @@ impl EditorEventRow {
|
|||
os_name: body.os_name.clone(),
|
||||
os_version: body.os_version.clone().unwrap_or_default(),
|
||||
architecture: body.architecture.clone(),
|
||||
system_id: body.system_id.clone().unwrap_or_default(),
|
||||
installation_id: body.installation_id.clone().unwrap_or_default(),
|
||||
metrics_id: body.metrics_id.clone().unwrap_or_default(),
|
||||
session_id: body.session_id.clone(),
|
||||
metrics_id: body.metrics_id.clone().unwrap_or_default(),
|
||||
is_staff: body.is_staff,
|
||||
time: time.timestamp_millis(),
|
||||
operation: event.operation,
|
||||
|
@ -699,6 +702,7 @@ impl EditorEventRow {
|
|||
#[derive(Serialize, Debug, clickhouse::Row)]
|
||||
pub struct InlineCompletionEventRow {
|
||||
installation_id: String,
|
||||
session_id: Option<String>,
|
||||
provider: String,
|
||||
suggestion_accepted: bool,
|
||||
app_version: String,
|
||||
|
@ -713,7 +717,6 @@ pub struct InlineCompletionEventRow {
|
|||
city: String,
|
||||
time: i64,
|
||||
is_staff: Option<bool>,
|
||||
session_id: Option<String>,
|
||||
major: Option<i32>,
|
||||
minor: Option<i32>,
|
||||
patch: Option<i32>,
|
||||
|
@ -879,7 +882,9 @@ impl AssistantEventRow {
|
|||
|
||||
#[derive(Debug, clickhouse::Row, Serialize)]
|
||||
pub struct CpuEventRow {
|
||||
system_id: Option<String>,
|
||||
installation_id: Option<String>,
|
||||
session_id: Option<String>,
|
||||
is_staff: Option<bool>,
|
||||
usage_as_percentage: f32,
|
||||
core_count: u32,
|
||||
|
@ -888,7 +893,6 @@ pub struct CpuEventRow {
|
|||
os_name: String,
|
||||
os_version: String,
|
||||
time: i64,
|
||||
session_id: Option<String>,
|
||||
// pub normalized_cpu_usage: f64, MATERIALIZED
|
||||
major: Option<i32>,
|
||||
minor: Option<i32>,
|
||||
|
@ -917,6 +921,7 @@ impl CpuEventRow {
|
|||
release_channel: body.release_channel.clone().unwrap_or_default(),
|
||||
os_name: body.os_name.clone(),
|
||||
os_version: body.os_version.clone().unwrap_or_default(),
|
||||
system_id: body.system_id.clone(),
|
||||
installation_id: body.installation_id.clone(),
|
||||
session_id: body.session_id.clone(),
|
||||
is_staff: body.is_staff,
|
||||
|
@ -940,6 +945,7 @@ pub struct MemoryEventRow {
|
|||
os_version: String,
|
||||
|
||||
// ClientEventBase
|
||||
system_id: Option<String>,
|
||||
installation_id: Option<String>,
|
||||
session_id: Option<String>,
|
||||
is_staff: Option<bool>,
|
||||
|
@ -971,6 +977,7 @@ impl MemoryEventRow {
|
|||
release_channel: body.release_channel.clone().unwrap_or_default(),
|
||||
os_name: body.os_name.clone(),
|
||||
os_version: body.os_version.clone().unwrap_or_default(),
|
||||
system_id: body.system_id.clone(),
|
||||
installation_id: body.installation_id.clone(),
|
||||
session_id: body.session_id.clone(),
|
||||
is_staff: body.is_staff,
|
||||
|
@ -994,6 +1001,7 @@ pub struct AppEventRow {
|
|||
os_version: String,
|
||||
|
||||
// ClientEventBase
|
||||
system_id: Option<String>,
|
||||
installation_id: Option<String>,
|
||||
session_id: Option<String>,
|
||||
is_staff: Option<bool>,
|
||||
|
@ -1024,6 +1032,7 @@ impl AppEventRow {
|
|||
release_channel: body.release_channel.clone().unwrap_or_default(),
|
||||
os_name: body.os_name.clone(),
|
||||
os_version: body.os_version.clone().unwrap_or_default(),
|
||||
system_id: body.system_id.clone(),
|
||||
installation_id: body.installation_id.clone(),
|
||||
session_id: body.session_id.clone(),
|
||||
is_staff: body.is_staff,
|
||||
|
@ -1046,6 +1055,7 @@ pub struct SettingEventRow {
|
|||
os_version: String,
|
||||
|
||||
// ClientEventBase
|
||||
system_id: Option<String>,
|
||||
installation_id: Option<String>,
|
||||
session_id: Option<String>,
|
||||
is_staff: Option<bool>,
|
||||
|
@ -1076,6 +1086,7 @@ impl SettingEventRow {
|
|||
release_channel: body.release_channel.clone().unwrap_or_default(),
|
||||
os_name: body.os_name.clone(),
|
||||
os_version: body.os_version.clone().unwrap_or_default(),
|
||||
system_id: body.system_id.clone(),
|
||||
installation_id: body.installation_id.clone(),
|
||||
session_id: body.session_id.clone(),
|
||||
is_staff: body.is_staff,
|
||||
|
@ -1099,6 +1110,7 @@ pub struct ExtensionEventRow {
|
|||
os_version: String,
|
||||
|
||||
// ClientEventBase
|
||||
system_id: Option<String>,
|
||||
installation_id: Option<String>,
|
||||
session_id: Option<String>,
|
||||
is_staff: Option<bool>,
|
||||
|
@ -1134,6 +1146,7 @@ impl ExtensionEventRow {
|
|||
release_channel: body.release_channel.clone().unwrap_or_default(),
|
||||
os_name: body.os_name.clone(),
|
||||
os_version: body.os_version.clone().unwrap_or_default(),
|
||||
system_id: body.system_id.clone(),
|
||||
installation_id: body.installation_id.clone(),
|
||||
session_id: body.session_id.clone(),
|
||||
is_staff: body.is_staff,
|
||||
|
@ -1224,6 +1237,7 @@ pub struct EditEventRow {
|
|||
os_version: String,
|
||||
|
||||
// ClientEventBase
|
||||
system_id: Option<String>,
|
||||
installation_id: Option<String>,
|
||||
// Note: This column name has a typo in the ClickHouse table.
|
||||
#[serde(rename = "sesssion_id")]
|
||||
|
@ -1261,6 +1275,7 @@ impl EditEventRow {
|
|||
release_channel: body.release_channel.clone().unwrap_or_default(),
|
||||
os_name: body.os_name.clone(),
|
||||
os_version: body.os_version.clone().unwrap_or_default(),
|
||||
system_id: body.system_id.clone(),
|
||||
installation_id: body.installation_id.clone(),
|
||||
session_id: body.session_id.clone(),
|
||||
is_staff: body.is_staff,
|
||||
|
|
|
@ -11,16 +11,14 @@ pub use smol;
|
|||
pub use sqlez;
|
||||
pub use sqlez_macros;
|
||||
|
||||
use release_channel::ReleaseChannel;
|
||||
pub use release_channel::RELEASE_CHANNEL;
|
||||
use sqlez::domain::Migrator;
|
||||
use sqlez::thread_safe_connection::ThreadSafeConnection;
|
||||
use sqlez_macros::sql;
|
||||
use std::env;
|
||||
use std::future::Future;
|
||||
use std::path::Path;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::LazyLock;
|
||||
use std::sync::{atomic::Ordering, LazyLock};
|
||||
use std::{env, sync::atomic::AtomicBool};
|
||||
use util::{maybe, ResultExt};
|
||||
|
||||
const CONNECTION_INITIALIZE_QUERY: &str = sql!(
|
||||
|
@ -47,16 +45,12 @@ pub static ALL_FILE_DB_FAILED: LazyLock<AtomicBool> = LazyLock::new(|| AtomicBoo
|
|||
/// This will retry a couple times if there are failures. If opening fails once, the db directory
|
||||
/// is moved to a backup folder and a new one is created. If that fails, a shared in memory db is created.
|
||||
/// In either case, static variables are set so that the user can be notified.
|
||||
pub async fn open_db<M: Migrator + 'static>(
|
||||
db_dir: &Path,
|
||||
release_channel: &ReleaseChannel,
|
||||
) -> ThreadSafeConnection<M> {
|
||||
pub async fn open_db<M: Migrator + 'static>(db_dir: &Path, scope: &str) -> ThreadSafeConnection<M> {
|
||||
if *ZED_STATELESS {
|
||||
return open_fallback_db().await;
|
||||
}
|
||||
|
||||
let release_channel_name = release_channel.dev_name();
|
||||
let main_db_dir = db_dir.join(Path::new(&format!("0-{}", release_channel_name)));
|
||||
let main_db_dir = db_dir.join(format!("0-{}", scope));
|
||||
|
||||
let connection = maybe!(async {
|
||||
smol::fs::create_dir_all(&main_db_dir)
|
||||
|
@ -118,7 +112,7 @@ pub async fn open_test_db<M: Migrator>(db_name: &str) -> ThreadSafeConnection<M>
|
|||
/// Implements a basic DB wrapper for a given domain
|
||||
#[macro_export]
|
||||
macro_rules! define_connection {
|
||||
(pub static ref $id:ident: $t:ident<()> = $migrations:expr;) => {
|
||||
(pub static ref $id:ident: $t:ident<()> = $migrations:expr; $($global:ident)?) => {
|
||||
pub struct $t($crate::sqlez::thread_safe_connection::ThreadSafeConnection<$t>);
|
||||
|
||||
impl ::std::ops::Deref for $t {
|
||||
|
@ -139,18 +133,23 @@ macro_rules! define_connection {
|
|||
}
|
||||
}
|
||||
|
||||
use std::sync::LazyLock;
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub static $id: LazyLock<$t> = LazyLock::new(|| {
|
||||
pub static $id: std::sync::LazyLock<$t> = std::sync::LazyLock::new(|| {
|
||||
$t($crate::smol::block_on($crate::open_test_db(stringify!($id))))
|
||||
});
|
||||
|
||||
#[cfg(not(any(test, feature = "test-support")))]
|
||||
pub static $id: LazyLock<$t> = LazyLock::new(|| {
|
||||
$t($crate::smol::block_on($crate::open_db($crate::database_dir(), &$crate::RELEASE_CHANNEL)))
|
||||
pub static $id: std::sync::LazyLock<$t> = std::sync::LazyLock::new(|| {
|
||||
let db_dir = $crate::database_dir();
|
||||
let scope = if false $(|| stringify!($global) == "global")? {
|
||||
"global"
|
||||
} else {
|
||||
$crate::RELEASE_CHANNEL.dev_name()
|
||||
};
|
||||
$t($crate::smol::block_on($crate::open_db(db_dir, scope)))
|
||||
});
|
||||
};
|
||||
(pub static ref $id:ident: $t:ident<$($d:ty),+> = $migrations:expr;) => {
|
||||
(pub static ref $id:ident: $t:ident<$($d:ty),+> = $migrations:expr; $($global:ident)?) => {
|
||||
pub struct $t($crate::sqlez::thread_safe_connection::ThreadSafeConnection<( $($d),+, $t )>);
|
||||
|
||||
impl ::std::ops::Deref for $t {
|
||||
|
@ -178,7 +177,13 @@ macro_rules! define_connection {
|
|||
|
||||
#[cfg(not(any(test, feature = "test-support")))]
|
||||
pub static $id: std::sync::LazyLock<$t> = std::sync::LazyLock::new(|| {
|
||||
$t($crate::smol::block_on($crate::open_db($crate::database_dir(), &$crate::RELEASE_CHANNEL)))
|
||||
let db_dir = $crate::database_dir();
|
||||
let scope = if false $(|| stringify!($global) == "global")? {
|
||||
"global"
|
||||
} else {
|
||||
$crate::RELEASE_CHANNEL.dev_name()
|
||||
};
|
||||
$t($crate::smol::block_on($crate::open_db(db_dir, scope)))
|
||||
});
|
||||
};
|
||||
}
|
||||
|
@ -225,7 +230,11 @@ mod tests {
|
|||
.prefix("DbTests")
|
||||
.tempdir()
|
||||
.unwrap();
|
||||
let _bad_db = open_db::<BadDB>(tempdir.path(), &release_channel::ReleaseChannel::Dev).await;
|
||||
let _bad_db = open_db::<BadDB>(
|
||||
tempdir.path(),
|
||||
&release_channel::ReleaseChannel::Dev.dev_name(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
/// Test that DB exists but corrupted (causing recreate)
|
||||
|
@ -262,13 +271,19 @@ mod tests {
|
|||
.tempdir()
|
||||
.unwrap();
|
||||
{
|
||||
let corrupt_db =
|
||||
open_db::<CorruptedDB>(tempdir.path(), &release_channel::ReleaseChannel::Dev).await;
|
||||
let corrupt_db = open_db::<CorruptedDB>(
|
||||
tempdir.path(),
|
||||
&release_channel::ReleaseChannel::Dev.dev_name(),
|
||||
)
|
||||
.await;
|
||||
assert!(corrupt_db.persistent());
|
||||
}
|
||||
|
||||
let good_db =
|
||||
open_db::<GoodDB>(tempdir.path(), &release_channel::ReleaseChannel::Dev).await;
|
||||
let good_db = open_db::<GoodDB>(
|
||||
tempdir.path(),
|
||||
&release_channel::ReleaseChannel::Dev.dev_name(),
|
||||
)
|
||||
.await;
|
||||
assert!(
|
||||
good_db.select_row::<usize>("SELECT * FROM test2").unwrap()()
|
||||
.unwrap()
|
||||
|
@ -311,8 +326,11 @@ mod tests {
|
|||
.unwrap();
|
||||
{
|
||||
// Setup the bad database
|
||||
let corrupt_db =
|
||||
open_db::<CorruptedDB>(tempdir.path(), &release_channel::ReleaseChannel::Dev).await;
|
||||
let corrupt_db = open_db::<CorruptedDB>(
|
||||
tempdir.path(),
|
||||
&release_channel::ReleaseChannel::Dev.dev_name(),
|
||||
)
|
||||
.await;
|
||||
assert!(corrupt_db.persistent());
|
||||
}
|
||||
|
||||
|
@ -323,7 +341,7 @@ mod tests {
|
|||
let guard = thread::spawn(move || {
|
||||
let good_db = smol::block_on(open_db::<GoodDB>(
|
||||
tmp_path.as_path(),
|
||||
&release_channel::ReleaseChannel::Dev,
|
||||
&release_channel::ReleaseChannel::Dev.dev_name(),
|
||||
));
|
||||
assert!(
|
||||
good_db.select_row::<usize>("SELECT * FROM test2").unwrap()()
|
||||
|
|
|
@ -60,3 +60,33 @@ mod tests {
|
|||
assert_eq!(db.read_kvp("key-1").unwrap(), None);
|
||||
}
|
||||
}
|
||||
|
||||
define_connection!(pub static ref GLOBAL_KEY_VALUE_STORE: GlobalKeyValueStore<()> =
|
||||
&[sql!(
|
||||
CREATE TABLE IF NOT EXISTS kv_store(
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT NOT NULL
|
||||
) STRICT;
|
||||
)];
|
||||
global
|
||||
);
|
||||
|
||||
impl GlobalKeyValueStore {
|
||||
query! {
|
||||
pub fn read_kvp(key: &str) -> Result<Option<String>> {
|
||||
SELECT value FROM kv_store WHERE key = (?)
|
||||
}
|
||||
}
|
||||
|
||||
query! {
|
||||
pub async fn write_kvp(key: String, value: String) -> Result<()> {
|
||||
INSERT OR REPLACE INTO kv_store(key, value) VALUES ((?), (?))
|
||||
}
|
||||
}
|
||||
|
||||
query! {
|
||||
pub async fn delete_kvp(key: String) -> Result<()> {
|
||||
DELETE FROM kv_store WHERE key = (?)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,8 +44,8 @@ const FEEDBACK_SUBMISSION_ERROR_TEXT: &str =
|
|||
struct FeedbackRequestBody<'a> {
|
||||
feedback_text: &'a str,
|
||||
email: Option<String>,
|
||||
metrics_id: Option<Arc<str>>,
|
||||
installation_id: Option<Arc<str>>,
|
||||
metrics_id: Option<Arc<str>>,
|
||||
system_specs: SystemSpecs,
|
||||
is_staff: bool,
|
||||
}
|
||||
|
@ -296,16 +296,16 @@ impl FeedbackModal {
|
|||
}
|
||||
|
||||
let telemetry = zed_client.telemetry();
|
||||
let metrics_id = telemetry.metrics_id();
|
||||
let installation_id = telemetry.installation_id();
|
||||
let metrics_id = telemetry.metrics_id();
|
||||
let is_staff = telemetry.is_staff();
|
||||
let http_client = zed_client.http_client();
|
||||
let feedback_endpoint = http_client.build_url("/api/feedback");
|
||||
let request = FeedbackRequestBody {
|
||||
feedback_text,
|
||||
email,
|
||||
metrics_id,
|
||||
installation_id,
|
||||
metrics_id,
|
||||
system_specs,
|
||||
is_staff: is_staff.unwrap_or(false),
|
||||
};
|
||||
|
|
|
@ -5,12 +5,14 @@ use std::{fmt::Display, sync::Arc, time::Duration};
|
|||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct EventRequestBody {
|
||||
/// Identifier unique to each system Zed is installed on
|
||||
pub system_id: Option<String>,
|
||||
/// Identifier unique to each Zed installation (differs for stable, preview, dev)
|
||||
pub installation_id: Option<String>,
|
||||
/// Identifier unique to each logged in Zed user (randomly generated on first sign in)
|
||||
pub metrics_id: Option<String>,
|
||||
/// Identifier unique to each Zed session (differs for each time you open Zed)
|
||||
pub session_id: Option<String>,
|
||||
pub metrics_id: Option<String>,
|
||||
/// True for Zed staff, otherwise false
|
||||
pub is_staff: Option<bool>,
|
||||
/// Zed version number
|
||||
|
@ -34,6 +36,7 @@ pub struct EventWrapper {
|
|||
pub signed_in: bool,
|
||||
/// Duration between this event's timestamp and the timestamp of the first event in the current batch
|
||||
pub milliseconds_since_first_event: i64,
|
||||
/// The event itself
|
||||
#[serde(flatten)]
|
||||
pub event: Event,
|
||||
}
|
||||
|
@ -245,8 +248,11 @@ pub struct Panic {
|
|||
pub architecture: String,
|
||||
/// The time the panic occurred (UNIX millisecond timestamp)
|
||||
pub panicked_on: i64,
|
||||
/// Identifier unique to each system Zed is installed on
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub system_id: Option<String>,
|
||||
/// Identifier unique to each Zed installation (differs for stable, preview, dev)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub installation_id: Option<String>,
|
||||
/// Identifier unique to each Zed session (differs for each time you open Zed)
|
||||
pub session_id: String,
|
||||
|
|
|
@ -13,7 +13,7 @@ use clap::{command, Parser};
|
|||
use cli::FORCE_CLI_MODE_ENV_VAR_NAME;
|
||||
use client::{parse_zed_link, Client, DevServerToken, ProxySettings, UserStore};
|
||||
use collab_ui::channel_view::ChannelView;
|
||||
use db::kvp::KEY_VALUE_STORE;
|
||||
use db::kvp::{GLOBAL_KEY_VALUE_STORE, KEY_VALUE_STORE};
|
||||
use editor::Editor;
|
||||
use env_logger::Builder;
|
||||
use fs::{Fs, RealFs};
|
||||
|
@ -334,19 +334,17 @@ fn main() {
|
|||
.with_assets(Assets)
|
||||
.with_http_client(IsahcHttpClient::new(None, None));
|
||||
|
||||
let (installation_id, existing_installation_id_found) = app
|
||||
.background_executor()
|
||||
.block(installation_id())
|
||||
.ok()
|
||||
.unzip();
|
||||
|
||||
let system_id = app.background_executor().block(system_id()).ok();
|
||||
let installation_id = app.background_executor().block(installation_id()).ok();
|
||||
let session_id = Uuid::new_v4().to_string();
|
||||
let session = app.background_executor().block(Session::new());
|
||||
|
||||
let app_version = AppVersion::init(env!("CARGO_PKG_VERSION"));
|
||||
|
||||
reliability::init_panic_hook(
|
||||
installation_id.clone(),
|
||||
app_version,
|
||||
session.id().to_owned(),
|
||||
system_id.as_ref().map(|id| id.to_string()),
|
||||
installation_id.as_ref().map(|id| id.to_string()),
|
||||
session_id.clone(),
|
||||
);
|
||||
|
||||
let (open_listener, mut open_rx) = OpenListener::new();
|
||||
|
@ -491,14 +489,26 @@ fn main() {
|
|||
client::init(&client, cx);
|
||||
language::init(cx);
|
||||
let telemetry = client.telemetry();
|
||||
telemetry.start(installation_id.clone(), session.id().to_owned(), cx);
|
||||
telemetry.report_app_event(
|
||||
match existing_installation_id_found {
|
||||
Some(false) => "first open",
|
||||
_ => "open",
|
||||
}
|
||||
.to_string(),
|
||||
telemetry.start(
|
||||
system_id.as_ref().map(|id| id.to_string()),
|
||||
installation_id.as_ref().map(|id| id.to_string()),
|
||||
session_id,
|
||||
cx,
|
||||
);
|
||||
if let (Some(system_id), Some(installation_id)) = (&system_id, &installation_id) {
|
||||
match (&system_id, &installation_id) {
|
||||
(IdType::New(_), IdType::New(_)) => {
|
||||
telemetry.report_app_event("first open".to_string());
|
||||
telemetry.report_app_event("first open for release channel".to_string());
|
||||
}
|
||||
(IdType::Existing(_), IdType::New(_)) => {
|
||||
telemetry.report_app_event("first open for release channel".to_string());
|
||||
}
|
||||
(_, IdType::Existing(_)) => {
|
||||
telemetry.report_app_event("open".to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
let app_session = cx.new_model(|cx| AppSession::new(session, cx));
|
||||
|
||||
let app_state = Arc::new(AppState {
|
||||
|
@ -514,7 +524,11 @@ fn main() {
|
|||
AppState::set_global(Arc::downgrade(&app_state), cx);
|
||||
|
||||
auto_update::init(client.http_client(), cx);
|
||||
reliability::init(client.http_client(), installation_id, cx);
|
||||
reliability::init(
|
||||
client.http_client(),
|
||||
installation_id.clone().map(|id| id.to_string()),
|
||||
cx,
|
||||
);
|
||||
let prompt_builder = init_common(app_state.clone(), cx);
|
||||
|
||||
let args = Args::parse();
|
||||
|
@ -755,7 +769,23 @@ async fn authenticate(client: Arc<Client>, cx: &AsyncAppContext) -> Result<()> {
|
|||
Ok::<_, anyhow::Error>(())
|
||||
}
|
||||
|
||||
async fn installation_id() -> Result<(String, bool)> {
|
||||
async fn system_id() -> Result<IdType> {
|
||||
let key_name = "system_id".to_string();
|
||||
|
||||
if let Ok(Some(system_id)) = GLOBAL_KEY_VALUE_STORE.read_kvp(&key_name) {
|
||||
return Ok(IdType::Existing(system_id));
|
||||
}
|
||||
|
||||
let system_id = Uuid::new_v4().to_string();
|
||||
|
||||
GLOBAL_KEY_VALUE_STORE
|
||||
.write_kvp(key_name, system_id.clone())
|
||||
.await?;
|
||||
|
||||
Ok(IdType::New(system_id))
|
||||
}
|
||||
|
||||
async fn installation_id() -> Result<IdType> {
|
||||
let legacy_key_name = "device_id".to_string();
|
||||
let key_name = "installation_id".to_string();
|
||||
|
||||
|
@ -765,11 +795,11 @@ async fn installation_id() -> Result<(String, bool)> {
|
|||
.write_kvp(key_name, installation_id.clone())
|
||||
.await?;
|
||||
KEY_VALUE_STORE.delete_kvp(legacy_key_name).await?;
|
||||
return Ok((installation_id, true));
|
||||
return Ok(IdType::Existing(installation_id));
|
||||
}
|
||||
|
||||
if let Ok(Some(installation_id)) = KEY_VALUE_STORE.read_kvp(&key_name) {
|
||||
return Ok((installation_id, true));
|
||||
return Ok(IdType::Existing(installation_id));
|
||||
}
|
||||
|
||||
let installation_id = Uuid::new_v4().to_string();
|
||||
|
@ -778,7 +808,7 @@ async fn installation_id() -> Result<(String, bool)> {
|
|||
.write_kvp(key_name, installation_id.clone())
|
||||
.await?;
|
||||
|
||||
Ok((installation_id, false))
|
||||
Ok(IdType::New(installation_id))
|
||||
}
|
||||
|
||||
async fn restore_or_create_workspace(
|
||||
|
@ -1087,6 +1117,20 @@ struct Args {
|
|||
dev_server_token: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
enum IdType {
|
||||
New(String),
|
||||
Existing(String),
|
||||
}
|
||||
|
||||
impl ToString for IdType {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
IdType::New(id) | IdType::Existing(id) => id.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_url_arg(arg: &str, cx: &AppContext) -> Result<String> {
|
||||
match std::fs::canonicalize(Path::new(&arg)) {
|
||||
Ok(path) => Ok(format!(
|
||||
|
|
|
@ -28,8 +28,9 @@ use crate::stdout_is_a_pty;
|
|||
static PANIC_COUNT: AtomicU32 = AtomicU32::new(0);
|
||||
|
||||
pub fn init_panic_hook(
|
||||
installation_id: Option<String>,
|
||||
app_version: SemanticVersion,
|
||||
system_id: Option<String>,
|
||||
installation_id: Option<String>,
|
||||
session_id: String,
|
||||
) {
|
||||
let is_pty = stdout_is_a_pty();
|
||||
|
@ -102,6 +103,7 @@ pub fn init_panic_hook(
|
|||
architecture: env::consts::ARCH.into(),
|
||||
panicked_on: Utc::now().timestamp_millis(),
|
||||
backtrace,
|
||||
system_id: system_id.clone(),
|
||||
installation_id: installation_id.clone(),
|
||||
session_id: session_id.clone(),
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue