Skip to content

Commit

Permalink
Allow frontend to send backend logs on request. (BloopAI#862)
Browse files Browse the repository at this point in the history
* Add tauri callback to return last logfile to the frontend

* add server log to crash reports and bug reports

---------

Co-authored-by: anastasiia <anastasiya1155@gmail.com>
  • Loading branch information
rsdy and anastasiya1155 committed Aug 17, 2023
1 parent 7ca3c7e commit 705a6e4
Show file tree
Hide file tree
Showing 7 changed files with 190 additions and 105 deletions.
267 changes: 169 additions & 98 deletions apps/desktop/src-tauri/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ use std::sync::Arc;
use bleep::{analytics, Application, Configuration, Environment};
use once_cell::sync::OnceCell;
use sentry::ClientInitGuard;
use tauri::{plugin::Plugin, Invoke};
use tracing::warn;

use super::{plugin, App, Manager, Payload, Runtime};
use super::{Manager, Payload, Runtime};

// a hack to get server/bleep/tests/desktop to run correctly
#[cfg(not(test))]
Expand All @@ -20,123 +22,192 @@ fn get_device_id() -> String {

static SENTRY: OnceCell<ClientInitGuard> = OnceCell::new();

pub(super) fn bleep<R>(app: &mut App<R>) -> plugin::Result<()>
#[tauri::command]
fn get_last_log_file(config: tauri::State<Configuration>) -> Option<String> {
let log_dir = config.log_dir();

let mut entries = std::fs::read_dir(log_dir)
.ok()?
.collect::<Result<Vec<_>, _>>()
.ok()?;

// Sort the entries by modified time (most recent first)
entries.sort_by_key(|entry| {
entry
.metadata()
.and_then(|m| m.modified())
.unwrap_or(std::time::SystemTime::UNIX_EPOCH)
});
entries.reverse();

// The first entry is the most recent log file
let filename = match entries.first() {
Some(path) => path.path().to_string_lossy().to_string(),
None => {
warn!("No log files found");
return None;
}
};

std::fs::read_to_string(filename).ok()
}

pub(super) struct BloopBackend<R>
where
R: Runtime,
{
let configuration = {
let path = app
.path_resolver()
.resolve_resource("config/config.json")
.expect("failed to resolve resource");

let mut bundled = Configuration::read(path).unwrap();
bundled.qdrant_url = Some("http://127.0.0.1:6334".into());
bundled.max_threads = bleep::default_parallelism() / 2;
bundled.model_dir = app
.path_resolver()
.resolve_resource("model")
.expect("bad bundle");

bundled.dylib_dir = Some(if cfg!(all(target_os = "macos", debug_assertions)) {
app.path_resolver()
.resolve_resource("dylibs")
.expect("missing `apps/desktop/src-tauri/dylibs`")
.parent()
.expect("invalid path")
.to_owned()
} else if cfg!(target_os = "macos") {
app.path_resolver()
.resolve_resource("dylibs")
.expect("missing `apps/desktop/src-tauri/dylibs`")
.parent()
.expect("invalid path")
.parent()
.expect("invalid path")
.join("Frameworks")
} else {
app.path_resolver()
.resolve_resource("dylibs")
.expect("missing `apps/desktop/src-tauri/dylibs`")
});
invoke_handler: Box<dyn Fn(Invoke<R>) + Send + Sync>,
}

let data_dir = app.path_resolver().app_data_dir().unwrap();
bundled.index_dir = data_dir.join("bleep");
impl<R: Runtime> BloopBackend<R> {
pub fn new() -> Self {
Self {
invoke_handler: Box::new(tauri::generate_handler![get_last_log_file]),
}
}
}

Configuration::merge(
bundled,
Configuration::cli_overriding_config_file().unwrap(),
)
};
impl<R: Runtime> Default for BloopBackend<R> {
fn default() -> Self {
Self::new()
}
}

Application::install_logging(&configuration);
impl<R: Runtime> Plugin<R> for BloopBackend<R> {
fn name(&self) -> &'static str {
"bleep"
}

if let Some(dsn) = &configuration.sentry_dsn {
initialize_sentry(dsn);
/// Extend the invoke handler.
fn extend_api(&mut self, message: Invoke<R>) {
(self.invoke_handler)(message)
}

let app = app.handle();
tokio::spawn(async move {
let initialized = Application::initialize(
Environment::insecure_local(),
configuration,
get_device_id(),
analytics::HubOptions {
event_filter: Some(Arc::new(|event| match *TELEMETRY.read().unwrap() {
true => Some(event),
false => None,
})),
package_metadata: Some(analytics::PackageMetadata {
name: env!("CARGO_CRATE_NAME"),
version: env!("CARGO_PKG_VERSION"),
git_rev: git_version::git_version!(fallback = "unknown"),
}),
},
)
.await;

if let Ok(backend) = initialized {
sentry::Hub::main().configure_scope(|scope| {
let backend = backend.clone();
scope.add_event_processor(move |mut event| {
event.user = Some(sentry_user()).map(|mut user| {
let auth = backend.user();
user.id = Some(
if let (Some(analytics), Some(username)) = (&backend.analytics, &auth) {
analytics.tracking_id(Some(username))
} else {
get_device_id()
},
);
user.username = auth;
user
});

Some(event)
fn initialize(
&mut self,
app: &tauri::AppHandle<R>,
_config: serde_json::Value,
) -> tauri::plugin::Result<()> {
let configuration = setup_configuration(app);

Application::install_logging(&configuration);

if let Some(dsn) = &configuration.sentry_dsn {
initialize_sentry(dsn);
}

app.manage(configuration.clone());

let app = app.clone();
tokio::spawn(start_backend(configuration, app));

Ok(())
}
}

async fn start_backend<R: Runtime>(configuration: Configuration, app: tauri::AppHandle<R>) {
let initialized = Application::initialize(
Environment::insecure_local(),
configuration,
get_device_id(),
analytics::HubOptions {
event_filter: Some(Arc::new(|event| match *TELEMETRY.read().unwrap() {
true => Some(event),
false => None,
})),
package_metadata: Some(analytics::PackageMetadata {
name: env!("CARGO_CRATE_NAME"),
version: env!("CARGO_PKG_VERSION"),
git_rev: git_version::git_version!(fallback = "unknown"),
}),
},
)
.await;

if let Ok(backend) = initialized {
sentry::Hub::main().configure_scope(|scope| {
let backend = backend.clone();
scope.add_event_processor(move |mut event| {
event.user = Some(sentry_user()).map(|mut user| {
let auth = backend.user();
user.id = Some(
if let (Some(analytics), Some(username)) = (&backend.analytics, &auth) {
analytics.tracking_id(Some(username))
} else {
get_device_id()
},
);
user.username = auth;
user
});

Some(event)
});
});

if let Err(_e) = backend.run().await {
app.emit_all(
"server-crashed",
Payload {
message: _e.to_string(),
},
)
.unwrap()
}
} else {
if let Err(_e) = backend.run().await {
app.emit_all(
"server-crashed",
Payload {
message: "Something bad happened".into(),
message: _e.to_string(),
},
)
.unwrap();
.unwrap()
}
} else {
app.emit_all(
"server-crashed",
Payload {
message: "Something bad happened".into(),
},
)
.unwrap();
}
}

fn setup_configuration<R: Runtime>(app: &tauri::AppHandle<R>) -> Configuration {
let path = app
.path_resolver()
.resolve_resource("config/config.json")
.expect("failed to resolve resource");

let mut bundled = Configuration::read(path).unwrap();
bundled.qdrant_url = Some("http://127.0.0.1:6334".into());
bundled.max_threads = bleep::default_parallelism() / 2;
bundled.model_dir = app
.path_resolver()
.resolve_resource("model")
.expect("bad bundle");

bundled.dylib_dir = Some(if cfg!(all(target_os = "macos", debug_assertions)) {
app.path_resolver()
.resolve_resource("dylibs")
.expect("missing `apps/desktop/src-tauri/dylibs`")
.parent()
.expect("invalid path")
.to_owned()
} else if cfg!(target_os = "macos") {
app.path_resolver()
.resolve_resource("dylibs")
.expect("missing `apps/desktop/src-tauri/dylibs`")
.parent()
.expect("invalid path")
.parent()
.expect("invalid path")
.join("Frameworks")
} else {
app.path_resolver()
.resolve_resource("dylibs")
.expect("missing `apps/desktop/src-tauri/dylibs`")
});

Ok(())
let data_dir = app.path_resolver().app_data_dir().unwrap();
bundled.index_dir = data_dir.join("bleep");

Configuration::merge(
bundled,
Configuration::cli_overriding_config_file().unwrap(),
)
}

fn initialize_sentry(dsn: &str) {
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ fn relative_command_path(command: impl AsRef<str>) -> Option<PathBuf> {
async fn main() {
tauri::Builder::default()
.plugin(qdrant::QdrantSupervisor::default())
.setup(backend::bleep)
.plugin(backend::BloopBackend::default())
.invoke_handler(tauri::generate_handler![
show_folder_in_finder,
enable_telemetry,
Expand Down
2 changes: 1 addition & 1 deletion client/src/CloudApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const CloudApp = () => {
platform: '',
version: '',
},
invokeTauriCommand: () => {},
invokeTauriCommand: () => Promise.resolve(''),
release: packageJson.version,
apiUrl: import.meta.env.API_URL || '/api',
isRepoManagementAllowed: true,
Expand Down
16 changes: 14 additions & 2 deletions client/src/components/ReportBugModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,13 @@ const ReportBugModal = ({
emailError: '',
});
const [isSubmitted, setSubmitted] = useState(false);
const [serverLog, setServerLog] = useState('');
const [serverCrashedMessage, setServerCrashedMessage] = useState('');
const { isBugReportModalOpen, setBugReportModalOpen } = useContext(
UIContext.BugReport,
);
const { envConfig, listen, os, release } = useContext(DeviceContext);
const { envConfig, listen, os, release, invokeTauriCommand } =
useContext(DeviceContext);
const { handleRemoveTab, setActiveTab, activeTab } = useContext(TabsContext);

const userForm = useMemo(
Expand All @@ -51,6 +53,14 @@ const ReportBugModal = ({
[],
);

useEffect(() => {
if (isBugReportModalOpen) {
invokeTauriCommand('plugin:bleep|get_last_log_file').then((log) => {
setServerLog(log);
});
}
}, [isBugReportModalOpen]);

useEffect(() => {
listen('server-crashed', (event) => {
console.log(event);
Expand Down Expand Up @@ -94,6 +104,7 @@ const ReportBugModal = ({
info: serverCrashedMessage,
metadata: JSON.stringify(os),
app_version: release,
server_log: serverLog,
});
} else {
const { emailError, ...values } = form;
Expand All @@ -102,11 +113,12 @@ const ReportBugModal = ({
unique_id: envConfig.tracking_id || '',
app_version: release,
metadata: JSON.stringify(os),
server_log: serverLog,
});
}
setSubmitted(true);
},
[form, envConfig.tracking_id, release],
[form, envConfig.tracking_id, release, serverLog],
);
const resetState = useCallback(() => {
if (serverCrashedMessage) {
Expand Down
4 changes: 2 additions & 2 deletions client/src/context/deviceContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export type DeviceContextType = {
platform: string;
version: string;
};
invokeTauriCommand: (c: string, payload?: any) => void;
invokeTauriCommand: (c: string, payload?: any) => Promise<any>;
relaunch: () => void;
release: string;
apiUrl: string;
Expand All @@ -44,7 +44,7 @@ export const DeviceContext = createContext<DeviceContextType>({
platform: '',
version: '',
},
invokeTauriCommand: () => {},
invokeTauriCommand: () => Promise.resolve(''),
relaunch: () => {},
release: '0.0.0',
apiUrl: '',
Expand Down
Loading

0 comments on commit 705a6e4

Please sign in to comment.