Skip to content

fix(logs): 退出流程关键节点补充日志,便于定位退出卡顿 - #437

Open
zmdyy0318 wants to merge 1 commit into
MaaXYZ:mainfrom
zmdyy0318:fix/exit-path-logs
Open

zmdyy0318 wants to merge 1 commit into
MaaXYZ:mainfrom
zmdyy0318:fix/exit-path-logs

Conversation

@zmdyy0318

@zmdyy0318 zmdyy0318 commented Nov 9, 2026 •

Copy link
Copy Markdown
Collaborator

背景

MaaEnd/MaaEnd#7020:任务链末尾执行关闭游戏 + MXU_KILLPROC(kill_self)后,MXU 主进程 2~3 分钟无响应才退出。
用户日志显示 agent 已在数秒内停止,MaaFramework 在卡顿期间没有任何 API 调用,最终是正常走到 RunEvent::Exit 退出的;
但退出流程中间没有日志,无法判断卡在 exit(0) 之前、窗口销毁,还是 agent 清理抢锁。

改动

在退出流程关键节点补充 [exit] 日志:

  • 前端 stopInstanceTasksAndExitApp 调用 exit(0) 前
  • WindowEvent::CloseRequested(是否最小化到托盘)
  • WindowEvent::Destroyed 清理开始 / 结束及耗时
  • cleanup_all_agent_children 获取 instances 锁的等待耗时
  • RunEvent::ExitRequested(退出码)、RunEvent::Exit
  • Windows 下 std::process::exit 前的最终退出码

仅新增日志,不改变行为。

AI 使用信息:

  • Agent:Claude Code
  • 模型:Claude Opus 5.5
  • 思考强度:中等

🤖 Generated with Claude Code

Sourcery 总结

增强:

  • 在应用程序关闭、窗口销毁、获取代理清理锁以及进程最终终止等环节添加结构化的退出流程日志,以便更好地诊断关闭延迟问题。
Original summary in English

Summary by Sourcery

Enhancements:

  • Add structured exit-flow logging around application shutdown, window destruction, agent cleanup lock acquisition, and final process termination to improve diagnosis of shutdown delays.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 9, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-09T13:32:16.967103Z 5d537c7 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5d537c7d55

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

return true;
}

log.info(`[task-stop#${instanceId}] 任务已停止,请求退出应用`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Await the pre-exit log write

In the Tauri exit path this call does not wait for the marker to reach disk: the logger method factory invokes the asynchronous writeLogToFile at src/utils/logger.ts:152-160 without returning its promise, and this function immediately proceeds to exit(0). On a fast exit or concurrent backend command dispatch, the WebView/process can be torn down before the plugin-fs append completes, so the marker intended to prove execution reached the exit request can be absent; use an awaited write/flush or a synchronous backend log command before requesting exit.

Useful? React with 👍 / 👎.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

嗨——我发现了 3 个问题

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="src-tauri/src/commands/types.rs" line_range="806-809" />
<code_context>
-        if let Ok(mut instances) = self.instances.lock() {
+        let lock_start = std::time::Instant::now();
+        let instances = self.instances.lock();
+        log::info!(
+            "[exit] Acquired instances lock for agent cleanup in {}ms",
+            lock_start.elapsed().as_millis()
+        );
+        if let Ok(mut instances) = instances {
             for (id, instance) in instances.iter_mut() {
</code_context>
<issue_to_address>
**Agent processes survive shutdown**

When another thread panicked while holding `instances`, `cleanup_all_agent_children` logs that it acquired the lock before checking the result, then `if let Ok` skips cleanup for the poisoned mutex; the window handler can report cleanup finished while agent children remain running.

Log acquisition only after a successful lock, and report or handle the poisoned-lock cleanup failure instead of silently skipping it.
</issue_to_address>

### Comment 2
<location path="src-tauri/src/lib.rs" line_range="378-382" />
<code_context>
-        if let Ok(mut instances) = self.instances.lock() {
+        let lock_start = std::time::Instant::now();
+        let instances = self.instances.lock();
+        log::info!(
+            "[exit] Acquired instances lock for agent cleanup in {}ms",
+            lock_start.elapsed().as_millis()
+        );
+        if let Ok(mut instances) = instances {
             for (id, instance) in instances.iter_mut() {
</code_context>
<issue_to_address>
**Slow log storage delays shutdown**

When the configured log folder or its filesystem stalls during a shutdown event, the folder log target writes synchronously on the event thread, so `log::info!` blocks close handling before `api.prevent_close()` or delays `RunEvent::Exit` cleanup until the log write returns; a stalled log filesystem therefore stalls the window or application exit.

Use a nonblocking log path for shutdown callbacks, or otherwise ensure a stalled log write cannot block event handling.

Also at `src-tauri/src/lib.rs:389-392`, `src-tauri/src/lib.rs:423`.
</issue_to_address>

### Comment 3
<location path="src/services/taskStopService.ts" line_range="98" />
<code_context>
     return true;
   }

+  log.info(`[task-stop#${instanceId}] 任务已停止,请求退出应用`);
   const { exit } = await import('@tauri-apps/plugin-process');
   await exit(0);
</code_context>
<issue_to_address>
**Exit marker disappears from logs**

When the app exits while the asynchronous file write is pending, `log.info` starts the file write without awaiting it, and `exit(0)` can terminate the process before it completes, so the pre-exit marker is absent from persisted logs.

Await completion of the log write before calling `exit(0)`.
</issue_to_address>

Sourcery 对开源项目免费——如果你喜欢我们的评审,欢迎考虑分享 ✨
Original comment in English

Hey - I've found 3 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="src-tauri/src/commands/types.rs" line_range="806-809" />
<code_context>
-        if let Ok(mut instances) = self.instances.lock() {
+        let lock_start = std::time::Instant::now();
+        let instances = self.instances.lock();
+        log::info!(
+            "[exit] Acquired instances lock for agent cleanup in {}ms",
+            lock_start.elapsed().as_millis()
+        );
+        if let Ok(mut instances) = instances {
             for (id, instance) in instances.iter_mut() {
</code_context>
<issue_to_address>
**Agent processes survive shutdown**

When another thread panicked while holding `instances`, `cleanup_all_agent_children` logs that it acquired the lock before checking the result, then `if let Ok` skips cleanup for the poisoned mutex; the window handler can report cleanup finished while agent children remain running.

Log acquisition only after a successful lock, and report or handle the poisoned-lock cleanup failure instead of silently skipping it.
</issue_to_address>

### Comment 2
<location path="src-tauri/src/lib.rs" line_range="378-382" />
<code_context>
-        if let Ok(mut instances) = self.instances.lock() {
+        let lock_start = std::time::Instant::now();
+        let instances = self.instances.lock();
+        log::info!(
+            "[exit] Acquired instances lock for agent cleanup in {}ms",
+            lock_start.elapsed().as_millis()
+        );
+        if let Ok(mut instances) = instances {
             for (id, instance) in instances.iter_mut() {
</code_context>
<issue_to_address>
**Slow log storage delays shutdown**

When the configured log folder or its filesystem stalls during a shutdown event, the folder log target writes synchronously on the event thread, so `log::info!` blocks close handling before `api.prevent_close()` or delays `RunEvent::Exit` cleanup until the log write returns; a stalled log filesystem therefore stalls the window or application exit.

Use a nonblocking log path for shutdown callbacks, or otherwise ensure a stalled log write cannot block event handling.

Also at `src-tauri/src/lib.rs:389-392`, `src-tauri/src/lib.rs:423`.
</issue_to_address>

### Comment 3
<location path="src/services/taskStopService.ts" line_range="98" />
<code_context>
     return true;
   }

+  log.info(`[task-stop#${instanceId}] 任务已停止,请求退出应用`);
   const { exit } = await import('@tauri-apps/plugin-process');
   await exit(0);
</code_context>
<issue_to_address>
**Exit marker disappears from logs**

When the app exits while the asynchronous file write is pending, `log.info` starts the file write without awaiting it, and `exit(0)` can terminate the process before it completes, so the pre-exit marker is absent from persisted logs.

Await completion of the log write before calling `exit(0)`.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment on lines +806 to +809
log::info!(
"[exit] Acquired instances lock for agent cleanup in {}ms",
lock_start.elapsed().as_millis()
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 中等 · Agent 进程在关闭后仍然存活

当另一个线程持有 instances 时发生 panic,cleanup_all_agent_children 会在检查锁结果前记录已获取锁;随后 if let Ok 会因互斥锁已中毒而跳过清理。这样一来,即使 agent 子进程仍在运行,窗口处理程序也可能报告清理已完成。

仅在成功获取锁后记录日志,并报告或处理因锁中毒导致的清理失败,而不是悄悄跳过。

面向 AI agent 的提示
在 `src-tauri/src/commands/types.rs` 第 806-809 行:

**Agent 进程在关闭后仍然存活**

当另一个线程持有 `instances` 时发生 panic,`cleanup_all_agent_children` 会在检查锁结果前记录已获取锁;随后 `if let Ok` 会因互斥锁已中毒而跳过清理。这样一来,即使 agent 子进程仍在运行,窗口处理程序也可能报告清理已完成。

仅在成功获取锁后记录日志,并报告或处理因锁中毒导致的清理失败,而不是悄悄跳过。
Original comment in English

🟡 Medium · Agent processes survive shutdown

When another thread panicked while holding instances, cleanup_all_agent_children logs that it acquired the lock before checking the result, then if let Ok skips cleanup for the poisoned mutex; the window handler can report cleanup finished while agent children remain running.

Log acquisition only after a successful lock, and report or handle the poisoned-lock cleanup failure instead of silently skipping it.

Prompt for AI agents
In `src-tauri/src/commands/types.rs` at lines 806-809:

**Agent processes survive shutdown**

When another thread panicked while holding `instances`, `cleanup_all_agent_children` logs that it acquired the lock before checking the result, then `if let Ok` skips cleanup for the poisoned mutex; the window handler can report cleanup finished while agent children remain running.

Log acquisition only after a successful lock, and report or handle the poisoned-lock cleanup failure instead of silently skipping it.

Comment thread src-tauri/src/lib.rs
Comment on lines +378 to +382
log::info!(
"[exit] Window '{}' close requested, minimize_to_tray={}",
window.label(),
minimize_to_tray
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 中等 · 日志存储缓慢会延迟关闭

当配置的日志目录或其文件系统在关闭事件期间发生卡顿时,目录日志目标会在事件线程上同步写入。因此,log::info! 会在调用 api.prevent_close() 之前阻塞关闭处理,或使 RunEvent::Exit 清理延迟到日志写入完成;因此,日志文件系统卡顿会导致窗口或应用程序退出受阻。

在关闭回调中使用非阻塞日志路径,或以其他方式确保日志写入卡顿不会阻塞事件处理。

另见 src-tauri/src/lib.rs:389-392、src-tauri/src/lib.rs:423。

面向 AI agent 的提示
在 `src-tauri/src/lib.rs` 第 378-382 行:

**日志存储缓慢会延迟关闭**

当配置的日志目录或其文件系统在关闭事件期间发生卡顿时,目录日志目标会在事件线程上同步写入。因此,`log::info!` 会在调用 `api.prevent_close()` 之前阻塞关闭处理,或使 `RunEvent::Exit` 清理延迟到日志写入完成;因此,日志文件系统卡顿会导致窗口或应用程序退出受阻。

在关闭回调中使用非阻塞日志路径,或以其他方式确保日志写入卡顿不会阻塞事件处理。

另见 `src-tauri/src/lib.rs:389-392`、`src-tauri/src/lib.rs:423`。
Original comment in English

🟡 Medium · Slow log storage delays shutdown

When the configured log folder or its filesystem stalls during a shutdown event, the folder log target writes synchronously on the event thread, so log::info! blocks close handling before api.prevent_close() or delays RunEvent::Exit cleanup until the log write returns; a stalled log filesystem therefore stalls the window or application exit.

Use a nonblocking log path for shutdown callbacks, or otherwise ensure a stalled log write cannot block event handling.

Also at src-tauri/src/lib.rs:389-392, src-tauri/src/lib.rs:423.

Prompt for AI agents
In `src-tauri/src/lib.rs` at lines 378-382:

**Slow log storage delays shutdown**

When the configured log folder or its filesystem stalls during a shutdown event, the folder log target writes synchronously on the event thread, so `log::info!` blocks close handling before `api.prevent_close()` or delays `RunEvent::Exit` cleanup until the log write returns; a stalled log filesystem therefore stalls the window or application exit.

Use a nonblocking log path for shutdown callbacks, or otherwise ensure a stalled log write cannot block event handling.

Also at `src-tauri/src/lib.rs:389-392`, `src-tauri/src/lib.rs:423`.

return true;
}

log.info(`[task-stop#${instanceId}] 任务已停止,请求退出应用`);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 中等 · 退出标记未写入日志

当应用程序退出时,如果异步文件写入仍在等待中,log.info 会启动文件写入但不会等待其完成,而 exit(0) 可能会在写入完成前终止进程,因此退出前的标记不会出现在持久化日志中。

在调用 exit(0) 之前等待日志写入完成。

面向 AI agent 的提示
在 `src/services/taskStopService.ts` 第 98 行:

**退出标记未写入日志**

当应用程序退出时,如果异步文件写入仍在等待中,`log.info` 会启动文件写入但不会等待其完成,而 `exit(0)` 可能会在写入完成前终止进程,因此退出前的标记不会出现在持久化日志中。

在调用 `exit(0)` 之前等待日志写入完成。
Original comment in English

🟡 Medium · Exit marker disappears from logs

When the app exits while the asynchronous file write is pending, log.info starts the file write without awaiting it, and exit(0) can terminate the process before it completes, so the pre-exit marker is absent from persisted logs.

Await completion of the log write before calling exit(0).

Prompt for AI agents
In `src/services/taskStopService.ts` at line 98:

**Exit marker disappears from logs**

When the app exits while the asynchronous file write is pending, `log.info` starts the file write without awaiting it, and `exit(0)` can terminate the process before it completes, so the pre-exit marker is absent from persisted logs.

Await completion of the log write before calling `exit(0)`.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant