aboutsummaryrefslogtreecommitdiff
path: root/src/app
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-04-23 00:48:22 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-04-23 00:48:22 +0300
commit40750418049c5125724b004c8f55ae66ca77edcf (patch)
treed72ecadb0506c3643ffaa5fe7cf40f22c90d967a /src/app
parent3d6f3e78624f1308a360cbf02eeca3fec2434436 (diff)
feat(logging): add robust rotating support logs
Diffstat (limited to 'src/app')
-rw-r--r--src/app/actions.rs27
-rw-r--r--src/app/api.rs82
-rw-r--r--src/app/config_io.rs2
-rw-r--r--src/app/creation.rs9
-rw-r--r--src/app/input/modal.rs22
-rw-r--r--src/app/mod.rs4
6 files changed, 104 insertions, 42 deletions
diff --git a/src/app/actions.rs b/src/app/actions.rs
index 33952b9f..b1bdfc63 100644
--- a/src/app/actions.rs
+++ b/src/app/actions.rs
@@ -105,6 +105,8 @@ impl AppState {
if idx < self.workspaces.len() {
self.active = Some(idx);
self.selected = idx;
+ let workspace_id = self.workspaces[idx].id.clone();
+ crate::logging::workspace_focused(&workspace_id);
self.mark_session_dirty();
if matches!(
self.agent_panel_scope,
@@ -114,7 +116,10 @@ impl AppState {
}
self.ensure_workspace_visible(idx);
if let Some(ws) = self.workspaces.get_mut(idx) {
- ws.switch_tab(ws.active_tab);
+ let active_tab = ws.active_tab;
+ ws.switch_tab(active_tab);
+ let tab_id = format!("{}:{}", workspace_id, active_tab + 1);
+ crate::logging::tab_focused(&workspace_id, &tab_id);
}
self.tab_scroll_follow_active = true;
self.refresh_tab_bar_view();
@@ -152,8 +157,14 @@ impl AppState {
}
pub fn switch_tab(&mut self, idx: usize) {
- if let Some(ws) = self.active.and_then(|i| self.workspaces.get_mut(i)) {
+ if let Some(ws_idx) = self.active {
+ let Some(ws) = self.workspaces.get_mut(ws_idx) else {
+ return;
+ };
ws.switch_tab(idx);
+ let workspace_id = ws.id.clone();
+ let tab_id = format!("{}:{}", workspace_id, idx + 1);
+ crate::logging::tab_focused(&workspace_id, &tab_id);
self.mark_session_dirty();
self.tab_scroll_follow_active = true;
self.refresh_tab_bar_view();
@@ -258,8 +269,8 @@ impl AppState {
return;
}
self.mark_session_dirty();
- let name = self.workspaces[self.selected].display_name();
- info!(workspace = %name, "workspace closed");
+ let workspace_id = self.workspaces[self.selected].id.clone();
+ crate::logging::workspace_closed(&workspace_id);
self.workspaces.remove(self.selected);
if self.workspaces.is_empty() {
self.active = None;
@@ -394,8 +405,14 @@ impl AppState {
self.close_selected_workspace();
return;
}
- if let Some(ws) = self.active.and_then(|i| self.workspaces.get_mut(i)) {
+ if let Some(ws_idx) = self.active {
+ let Some(ws) = self.workspaces.get_mut(ws_idx) else {
+ return;
+ };
+ let workspace_id = ws.id.clone();
+ let closing_tab_id = format!("{}:{}", workspace_id, ws.active_tab + 1);
ws.close_active_tab();
+ crate::logging::tab_closed(&workspace_id, &closing_tab_id);
self.tab_scroll_follow_active = true;
self.refresh_tab_bar_view();
}
diff --git a/src/app/api.rs b/src/app/api.rs
index b07c1267..364097dc 100644
--- a/src/app/api.rs
+++ b/src/app/api.rs
@@ -333,6 +333,7 @@ impl App {
if let Some(label) = params.label {
if let Some(workspace) = self.state.workspaces.get_mut(index) {
workspace.set_custom_name(label);
+ crate::logging::workspace_renamed(&workspace.id);
}
}
let workspace = self.workspace_info(index);
@@ -428,6 +429,7 @@ impl App {
.unwrap();
};
ws.set_custom_name(params.label.clone());
+ crate::logging::workspace_renamed(&ws.id);
self.schedule_session_save();
self.emit_event(crate::api::schema::EventEnvelope {
event: crate::api::schema::EventKind::WorkspaceRenamed,
@@ -604,6 +606,10 @@ impl App {
match result {
Ok(tab_idx) => {
if let Some(label) = label {
+ let workspace_id = self.state.workspaces[ws_idx].id.clone();
+ let tab_id = self
+ .public_tab_id(ws_idx, tab_idx)
+ .unwrap_or_else(|| format!("{}:{}", workspace_id, tab_idx + 1));
if let Some(tab) = self
.state
.workspaces
@@ -611,6 +617,7 @@ impl App {
.and_then(|ws| ws.tabs.get_mut(tab_idx))
{
tab.set_custom_name(label);
+ crate::logging::tab_renamed(&workspace_id, &tab_id);
}
}
if focus {
@@ -682,6 +689,10 @@ impl App {
})
.unwrap();
};
+ let workspace_id = self.state.workspaces[ws_idx].id.clone();
+ let tab_id = self
+ .public_tab_id(ws_idx, tab_idx)
+ .unwrap_or_else(|| format!("{}:{}", workspace_id, tab_idx + 1));
let Some(tab) = self
.state
.workspaces
@@ -698,6 +709,7 @@ impl App {
.unwrap();
};
tab.set_custom_name(params.label.clone());
+ crate::logging::tab_renamed(&workspace_id, &tab_id);
self.schedule_session_save();
self.emit_event(crate::api::schema::EventEnvelope {
event: crate::api::schema::EventKind::TabRenamed,
@@ -1237,6 +1249,7 @@ impl App {
});
match path {
Ok(path) => {
+ crate::logging::integration_action("install", "pi", "ok");
vec![format!("installed pi integration to {}", path.display())]
}
Err(response) => return response,
@@ -1254,16 +1267,19 @@ impl App {
.unwrap()
});
match installed {
- Ok(installed) => vec![
- format!(
- "installed claude integration hook to {}",
- installed.hook_path.display()
- ),
- format!(
- "ensured claude settings at {}",
- installed.settings_path.display()
- ),
- ],
+ Ok(installed) => {
+ crate::logging::integration_action("install", "claude", "ok");
+ vec![
+ format!(
+ "installed claude integration hook to {}",
+ installed.hook_path.display()
+ ),
+ format!(
+ "ensured claude settings at {}",
+ installed.settings_path.display()
+ ),
+ ]
+ }
Err(response) => return response,
}
}
@@ -1279,20 +1295,23 @@ impl App {
.unwrap()
});
match installed {
- Ok(installed) => vec![
- format!(
- "installed codex integration hook to {}",
- installed.hook_path.display()
- ),
- format!(
- "ensured codex hooks at {}",
- installed.hooks_path.display()
- ),
- format!(
- "ensured codex config at {}",
- installed.config_path.display()
- ),
- ],
+ Ok(installed) => {
+ crate::logging::integration_action("install", "codex", "ok");
+ vec![
+ format!(
+ "installed codex integration hook to {}",
+ installed.hook_path.display()
+ ),
+ format!(
+ "ensured codex hooks at {}",
+ installed.hooks_path.display()
+ ),
+ format!(
+ "ensured codex config at {}",
+ installed.config_path.display()
+ ),
+ ]
+ }
Err(response) => return response,
}
}
@@ -1308,10 +1327,13 @@ impl App {
.unwrap()
});
match installed {
- Ok(installed) => vec![format!(
- "installed opencode integration plugin to {}",
- installed.plugin_path.display()
- )],
+ Ok(installed) => {
+ crate::logging::integration_action("install", "opencode", "ok");
+ vec![format!(
+ "installed opencode integration plugin to {}",
+ installed.plugin_path.display()
+ )]
+ }
Err(response) => return response,
}
}
@@ -1341,6 +1363,7 @@ impl App {
});
match result {
Ok(result) => {
+ crate::logging::integration_action("uninstall", "pi", "ok");
if result.removed_extension {
vec![format!(
"removed pi integration extension at {}",
@@ -1369,6 +1392,7 @@ impl App {
});
match result {
Ok(result) => {
+ crate::logging::integration_action("uninstall", "claude", "ok");
let mut messages = Vec::new();
if result.removed_hook_file {
messages.push(format!(
@@ -1410,6 +1434,7 @@ impl App {
});
match result {
Ok(result) => {
+ crate::logging::integration_action("uninstall", "codex", "ok");
let mut messages = Vec::new();
if result.removed_hook_file {
messages.push(format!(
@@ -1455,6 +1480,7 @@ impl App {
});
match result {
Ok(result) => {
+ crate::logging::integration_action("uninstall", "opencode", "ok");
if result.removed_plugin {
vec![format!(
"removed opencode integration plugin at {}",
diff --git a/src/app/config_io.rs b/src/app/config_io.rs
index 5d4b2b13..6770d61e 100644
--- a/src/app/config_io.rs
+++ b/src/app/config_io.rs
@@ -8,6 +8,7 @@ impl App {
let path = crate::config::config_path();
if let Some(parent) = path.parent() {
if let Err(err) = std::fs::create_dir_all(parent) {
+ crate::logging::config_write_failed(&path, error_context, &err.to_string());
self.state.config_diagnostic =
Some(format!("failed to save {error_context}: {err}"));
self.config_diagnostic_deadline =
@@ -19,6 +20,7 @@ impl App {
let content = std::fs::read_to_string(&path).unwrap_or_default();
let new_content = update(&content);
if let Err(err) = std::fs::write(&path, new_content) {
+ crate::logging::config_write_failed(&path, error_context, &err.to_string());
self.state.config_diagnostic = Some(format!("failed to save {error_context}: {err}"));
self.config_diagnostic_deadline =
Some(std::time::Instant::now() + std::time::Duration::from_secs(5));
diff --git a/src/app/creation.rs b/src/app/creation.rs
index dd2833e0..508bf023 100644
--- a/src/app/creation.rs
+++ b/src/app/creation.rs
@@ -89,6 +89,12 @@ impl App {
ws.switch_tab(idx);
self.state.mode = Mode::Terminal;
}
+ let workspace_id = self.state.workspaces[ws_idx].id.clone();
+ let tab_id = self
+ .public_tab_id(ws_idx, idx)
+ .unwrap_or_else(|| format!("{}:{}", workspace_id, idx + 1));
+ let root_pane = self.state.workspaces[ws_idx].tabs[idx].root_pane.raw();
+ crate::logging::tab_created(&workspace_id, &tab_id, root_pane);
self.schedule_session_save();
Ok(idx)
}
@@ -112,6 +118,9 @@ impl App {
ws.refresh_git_ahead_behind();
self.state.workspaces.push(ws);
let idx = self.state.workspaces.len() - 1;
+ let workspace_id = self.state.workspaces[idx].id.clone();
+ let root_pane = self.state.workspaces[idx].tabs[0].root_pane.raw();
+ crate::logging::workspace_created(&workspace_id, root_pane);
if focus || self.state.active.is_none() {
self.state.switch_workspace(idx);
self.state.mode = Mode::Terminal;
diff --git a/src/app/input/modal.rs b/src/app/input/modal.rs
index 5c3108f2..aa25ea1e 100644
--- a/src/app/input/modal.rs
+++ b/src/app/input/modal.rs
@@ -263,7 +263,9 @@ pub(super) fn apply_rename_action(state: &mut AppState, action: ModalAction) {
match state.mode {
Mode::RenameWorkspace if !state.workspaces.is_empty() => {
if !new_name.is_empty() {
+ let workspace_id = state.workspaces[state.selected].id.clone();
state.workspaces[state.selected].set_custom_name(new_name);
+ crate::logging::workspace_renamed(&workspace_id);
state.mark_session_dirty();
}
}
@@ -278,13 +280,19 @@ pub(super) fn apply_rename_action(state: &mut AppState, action: ModalAction) {
};
}
Mode::RenameTab => {
- if let Some(ws) = state.active.and_then(|i| state.workspaces.get_mut(i)) {
- if let Some(tab) = ws.active_tab_mut() {
- let keep_auto_name =
- tab.is_auto_named() && new_name == tab.number.to_string();
- if !new_name.is_empty() && !keep_auto_name {
- tab.set_custom_name(new_name);
- state.mark_session_dirty();
+ if let Some(ws_idx) = state.active {
+ if let Some(ws) = state.workspaces.get_mut(ws_idx) {
+ let workspace_id = ws.id.clone();
+ let active_tab = ws.active_tab;
+ if let Some(tab) = ws.active_tab_mut() {
+ let keep_auto_name =
+ tab.is_auto_named() && new_name == tab.number.to_string();
+ if !new_name.is_empty() && !keep_auto_name {
+ tab.set_custom_name(new_name);
+ let tab_id = format!("{}:{}", workspace_id, active_tab + 1);
+ crate::logging::tab_renamed(&workspace_id, &tab_id);
+ state.mark_session_dirty();
+ }
}
}
}
diff --git a/src/app/mod.rs b/src/app/mod.rs
index 78681e06..14c50f7f 100644
--- a/src/app/mod.rs
+++ b/src/app/mod.rs
@@ -182,7 +182,7 @@ impl App {
render_dirty.clone(),
);
if ws.is_empty() {
- info!("session file found but no workspaces restored");
+ crate::logging::session_restored(0, "empty");
(
Vec::new(),
None,
@@ -192,7 +192,7 @@ impl App {
snap.sidebar_section_split.unwrap_or(0.5),
)
} else {
- info!(count = ws.len(), "session restored");
+ crate::logging::session_restored(ws.len(), "ok");
let active = snap.active.filter(|&i| i < ws.len());
let selected = snap.selected.min(ws.len().saturating_sub(1));
(