v0.18.12:出問題時一鍵打開紀錄檔資料夾(leo:「不能用一個 debug mode?」)

leo 08-06:「不能用一個 debug mode,就是它會把 log 寫在一個檔案?」

## 回答:log 一直都在寫,缺的是「找得到」
`~/.arcrun-rag/collector.log`(含 collector 的 stderr,以 `[stderr]` 開頭)
與 `app.log` 從 t14 就在寫。這次 Windows 事故的真正死因
(`config 缺必填欄位:manifest`)**一直躺在 collector.log 裡**,
但使用者只看得到畫面上一句話,沒有任何入口通到那些檔案。

⇒ **不做「debug mode 開關」**(多一個要教使用者的東西,而且出事當下才想到要開就來不及了),
   改成**永遠寫、出事時一鍵打開**:
- `OpenLogFolder()` 綁定(Windows 走 explorer/Mac 走 open/其餘 xdg-open)
- 首頁在 `engineTrouble` 時才長出「需要回報這個問題?」卡,附按鈕與**路徑文字**
  (按鈕失效時仍找得到)。沒事時不顯示——否則會變常駐噪音,真出事反而沒人看。

## 真機證據:v0.18.10 的「說出死因」有效
leo 的 Windows 畫面實拍:
  「同步引擎一直啟動失敗 / 已自動重試 8 次都失敗,所以重新開啟也沒有用。
    原因:collector direct: config 缺必填欄位:manifest(exit status 2)」
⇒ **與 v0.18.11 修的根因一字不差**,診斷鏈完整閉合:
   症狀(閃爍)→ 機制(子行程 exit 2 無限重拉)→ 死因(config 缺 manifest)→ 修正。
This commit is contained in:
2026-08-06 18:47:17 +08:00
parent fcf00bb5df
commit 9f64615cfd
3 changed files with 43 additions and 1 deletions
+2 -1
View File
@@ -1,5 +1,6 @@
{
"v0.18.9": "8be9260eb542f527",
"v0.18.10": "a440f044a849b7f2",
"v0.18.11": "1538689e181444f6"
"v0.18.11": "1538689e181444f6",
"v0.18.12": "ce37982f05d8cb5b"
}
+40
View File
@@ -21,7 +21,9 @@ import (
"encoding/json"
"fmt"
"os"
"os/exec"
"path/filepath"
runtime2 "runtime"
"sort"
"strings"
"time"
@@ -196,6 +198,10 @@ type UIState struct {
ExtractedOK int `json:"extractedOK"` // 首頁「已整理幾份」
Steps []Step `json:"steps"` // 首頁狀態時間軸(leo #6
Skipped *UISkipped `json:"skipped"` // 讀不了的檔(沒有就是 null,前端不畫)
// EngineTrouble=同步引擎有問題(沒在跑/一直啟動失敗)⇒ 前端才長出「回報問題」卡。
// 沒事時不顯示,避免把「哪裡看 log」變成常駐噪音。
EngineTrouble bool `json:"engineTrouble"`
LogFolder string `json:"logFolder"`
}
// UISkipped=首頁那張「這些檔案現在還處理不了」的卡。
@@ -343,6 +349,10 @@ func (a *App) GetState() UIState {
st.Syncing, st.StatusBig, st.StatusSub = describeStatus(sync)
st.Steps = buildSteps(sync, st.Syncing)
st.Skipped = buildSkipped(sync)
// 引擎有問題才把「回報問題」卡叫出來(含記錄檔路徑)。
// 沒事時不顯示——否則「哪裡看 log」會變成常駐噪音,真出事時反而沒人看。
st.EngineTrouble = !collectorAlive()
st.LogFolder = appDir()
return st
}
@@ -543,6 +553,36 @@ func (a *App) SetAI(useGemini bool, key string) error {
// OpenURL 用系統瀏覽器開網址(下載頁/說明文件)。
func (a *App) OpenURL(u string) { runtime.BrowserOpenURL(a.ctx, u) }
// OpenLogFolder 在檔案總管/Finder 裡打開記錄檔資料夾。
//
// 🔴 leo 2026-08-06:「不能用一個 debug mode,就是它會把 log 寫在一個檔案?」
//
// 答:**log 一直都有寫**`~/.arcrun-rag/` 下的 collector.log 與 app.log
// collector 的 stderr 也以 `[stderr]` 開頭落在裡面)。
// 缺的不是「有沒有寫」,是**使用者找不到**——出事時他只看得到畫面上那句話,
// 沒有任何入口把他帶到檔案。這次 Windows 事故就卡在這:真正的死因
// config 缺 manifest)一直躺在 collector.log 裡,沒人看得到。
// ⇒ 不做「debug mode 開關」(多一個要教使用者的東西),而是**永遠寫、一鍵打開**。
func (a *App) OpenLogFolder() error {
dir := appDir()
if err := os.MkdirAll(dir, 0o755); err != nil {
return err
}
switch runtime2.GOOS {
case "windows":
// explorer 開啟後回傳非 0 是常態(它不等視窗關閉),所以不看 error。
_ = exec.Command("explorer", dir).Start()
case "darwin":
return exec.Command("open", dir).Start()
default:
return exec.Command("xdg-open", dir).Start()
}
return nil
}
// LogFolderPath 給畫面顯示用(讓使用者就算按鈕失效也知道去哪找)。
func (a *App) LogFolderPath() string { return appDir() }
// ── 托盤會呼叫的兩個動作(t194)──
// ShowWindow 把主視窗叫出來並帶到前景。
+1
View File
@@ -213,6 +213,7 @@ function renderPage() {
function wire() {
const on = (id, fn) => { const e = $(id); if (e) e.onclick = fn; };
on('uDocs', () => go.OpenURL('https://rag.arcrun.dev/docs/'));
on('hLogs', () => go.OpenLogFolder());
on('hAcct', showConnect); on('obConnect', showConnect);
on('obInstall', () => go.OpenURL('https://install.arcrun.dev/'));
on('aiSave', saveAI);