Implement panic reporting saving and uploads (#19932)
TODO: - [x] check that the app version is well formatted for zed.dev Release Notes: - N/A --------- Co-authored-by: Trace <violet.white.batt@gmail.com>
This commit is contained in:
parent
a5f52f0f04
commit
b3f0ba1430
10 changed files with 278 additions and 45 deletions
|
@ -1,13 +1,14 @@
|
|||
use anyhow::{Context, Result};
|
||||
use backtrace::{self, Backtrace};
|
||||
use chrono::Utc;
|
||||
use client::telemetry;
|
||||
use client::{telemetry, TelemetrySettings};
|
||||
use db::kvp::KEY_VALUE_STORE;
|
||||
use gpui::{AppContext, SemanticVersion};
|
||||
use http_client::{HttpRequestExt, Method};
|
||||
|
||||
use http_client::{self, HttpClient, HttpClientWithUrl};
|
||||
use paths::{crashes_dir, crashes_retired_dir};
|
||||
use project::Project;
|
||||
use release_channel::ReleaseChannel;
|
||||
use release_channel::RELEASE_CHANNEL;
|
||||
use settings::Settings;
|
||||
|
@ -21,6 +22,7 @@ use std::{io::Write, panic, sync::atomic::AtomicU32, thread};
|
|||
use telemetry_events::LocationData;
|
||||
use telemetry_events::Panic;
|
||||
use telemetry_events::PanicRequest;
|
||||
use url::Url;
|
||||
use util::ResultExt;
|
||||
|
||||
use crate::stdout_is_a_pty;
|
||||
|
@ -133,13 +135,73 @@ pub fn init_panic_hook(
|
|||
|
||||
pub fn init(
|
||||
http_client: Arc<HttpClientWithUrl>,
|
||||
system_id: Option<String>,
|
||||
installation_id: Option<String>,
|
||||
session_id: String,
|
||||
cx: &mut AppContext,
|
||||
) {
|
||||
#[cfg(target_os = "macos")]
|
||||
monitor_main_thread_hangs(http_client.clone(), installation_id.clone(), cx);
|
||||
|
||||
upload_panics_and_crashes(http_client, installation_id, cx)
|
||||
let Some(panic_report_url) = http_client
|
||||
.build_zed_api_url("/telemetry/panics", &[])
|
||||
.log_err()
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
upload_panics_and_crashes(
|
||||
http_client.clone(),
|
||||
panic_report_url.clone(),
|
||||
installation_id.clone(),
|
||||
cx,
|
||||
);
|
||||
|
||||
cx.observe_new_models(move |project: &mut Project, cx| {
|
||||
let http_client = http_client.clone();
|
||||
let panic_report_url = panic_report_url.clone();
|
||||
let session_id = session_id.clone();
|
||||
let installation_id = installation_id.clone();
|
||||
let system_id = system_id.clone();
|
||||
|
||||
if let Some(ssh_client) = project.ssh_client() {
|
||||
ssh_client.update(cx, |client, cx| {
|
||||
if TelemetrySettings::get_global(cx).diagnostics {
|
||||
let request = client.proto_client().request(proto::GetPanicFiles {});
|
||||
cx.background_executor()
|
||||
.spawn(async move {
|
||||
let panic_files = request.await?;
|
||||
for file in panic_files.file_contents {
|
||||
let panic: Option<Panic> = serde_json::from_str(&file)
|
||||
.log_err()
|
||||
.or_else(|| {
|
||||
file.lines()
|
||||
.next()
|
||||
.and_then(|line| serde_json::from_str(line).ok())
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
log::error!("failed to deserialize panic file {:?}", file);
|
||||
None
|
||||
});
|
||||
|
||||
if let Some(mut panic) = panic {
|
||||
panic.session_id = session_id.clone();
|
||||
panic.system_id = system_id.clone();
|
||||
panic.installation_id = installation_id.clone();
|
||||
|
||||
upload_panic(&http_client, &panic_report_url, panic, &mut None)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
anyhow::Ok(())
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
|
@ -346,16 +408,18 @@ pub fn monitor_main_thread_hangs(
|
|||
|
||||
fn upload_panics_and_crashes(
|
||||
http: Arc<HttpClientWithUrl>,
|
||||
panic_report_url: Url,
|
||||
installation_id: Option<String>,
|
||||
cx: &AppContext,
|
||||
) {
|
||||
let telemetry_settings = *client::TelemetrySettings::get_global(cx);
|
||||
cx.background_executor()
|
||||
.spawn(async move {
|
||||
let most_recent_panic = upload_previous_panics(http.clone(), telemetry_settings)
|
||||
.await
|
||||
.log_err()
|
||||
.flatten();
|
||||
let most_recent_panic =
|
||||
upload_previous_panics(http.clone(), &panic_report_url, telemetry_settings)
|
||||
.await
|
||||
.log_err()
|
||||
.flatten();
|
||||
upload_previous_crashes(http, most_recent_panic, installation_id, telemetry_settings)
|
||||
.await
|
||||
.log_err()
|
||||
|
@ -366,9 +430,9 @@ fn upload_panics_and_crashes(
|
|||
/// Uploads panics via `zed.dev`.
|
||||
async fn upload_previous_panics(
|
||||
http: Arc<HttpClientWithUrl>,
|
||||
panic_report_url: &Url,
|
||||
telemetry_settings: client::TelemetrySettings,
|
||||
) -> Result<Option<(i64, String)>> {
|
||||
let panic_report_url = http.build_zed_api_url("/telemetry/panics", &[])?;
|
||||
) -> anyhow::Result<Option<(i64, String)>> {
|
||||
let mut children = smol::fs::read_dir(paths::logs_dir()).await?;
|
||||
|
||||
let mut most_recent_panic = None;
|
||||
|
@ -396,7 +460,7 @@ async fn upload_previous_panics(
|
|||
.context("error reading panic file")?;
|
||||
|
||||
let panic: Option<Panic> = serde_json::from_str(&panic_file_content)
|
||||
.ok()
|
||||
.log_err()
|
||||
.or_else(|| {
|
||||
panic_file_content
|
||||
.lines()
|
||||
|
@ -409,26 +473,8 @@ async fn upload_previous_panics(
|
|||
});
|
||||
|
||||
if let Some(panic) = panic {
|
||||
most_recent_panic = Some((panic.panicked_on, panic.payload.clone()));
|
||||
|
||||
let json_bytes = serde_json::to_vec(&PanicRequest { panic }).unwrap();
|
||||
|
||||
let Some(checksum) = client::telemetry::calculate_json_checksum(&json_bytes) else {
|
||||
if !upload_panic(&http, &panic_report_url, panic, &mut most_recent_panic).await? {
|
||||
continue;
|
||||
};
|
||||
|
||||
let Ok(request) = http_client::Request::builder()
|
||||
.method(Method::POST)
|
||||
.uri(panic_report_url.as_ref())
|
||||
.header("x-zed-checksum", checksum)
|
||||
.body(json_bytes.into())
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let response = http.send(request).await.context("error sending panic")?;
|
||||
if !response.status().is_success() {
|
||||
log::error!("Error uploading panic to server: {}", response.status());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -438,9 +484,42 @@ async fn upload_previous_panics(
|
|||
.context("error removing panic")
|
||||
.log_err();
|
||||
}
|
||||
Ok::<_, anyhow::Error>(most_recent_panic)
|
||||
Ok(most_recent_panic)
|
||||
}
|
||||
|
||||
async fn upload_panic(
|
||||
http: &Arc<HttpClientWithUrl>,
|
||||
panic_report_url: &Url,
|
||||
panic: telemetry_events::Panic,
|
||||
most_recent_panic: &mut Option<(i64, String)>,
|
||||
) -> Result<bool> {
|
||||
*most_recent_panic = Some((panic.panicked_on, panic.payload.clone()));
|
||||
|
||||
let json_bytes = serde_json::to_vec(&PanicRequest {
|
||||
panic: panic.clone(),
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let Some(checksum) = client::telemetry::calculate_json_checksum(&json_bytes) else {
|
||||
return Ok(false);
|
||||
};
|
||||
|
||||
let Ok(request) = http_client::Request::builder()
|
||||
.method(Method::POST)
|
||||
.uri(panic_report_url.as_ref())
|
||||
.header("x-zed-checksum", checksum)
|
||||
.body(json_bytes.into())
|
||||
else {
|
||||
return Ok(false);
|
||||
};
|
||||
|
||||
let response = http.send(request).await.context("error sending panic")?;
|
||||
if !response.status().is_success() {
|
||||
log::error!("Error uploading panic to server: {}", response.status());
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
const LAST_CRASH_UPLOADED: &str = "LAST_CRASH_UPLOADED";
|
||||
|
||||
/// upload crashes from apple's diagnostic reports to our server.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue