From 74c6d5606b893f69c1d41550fc4849ada9a5d7c8 Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Mon, 18 May 2026 23:20:38 +0200 Subject: [PATCH] fix(xray): rotate crash reports into log folder, prevent overwrites writeCrashReport had two flaws: it wrote to the bin folder (alongside the xray binary) which conflates artifacts, and the second-precision timestamp meant a tight restart-loop crash burst overwrote prior reports. Write to the log folder with nanosecond precision and keep the last 10 reports. Co-Authored-By: Claude Opus 4.7 --- xray/process.go | 42 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/xray/process.go b/xray/process.go index 83e84135..89bd5fe5 100644 --- a/xray/process.go +++ b/xray/process.go @@ -7,7 +7,9 @@ import ( "fmt" "os" "os/exec" + "path/filepath" "runtime" + "sort" "strings" "sync" "sync/atomic" @@ -453,8 +455,42 @@ func (p *process) waitForExit(timeout time.Duration) error { } } -// writeCrashReport writes a crash report to the binary folder with a timestamped filename. +const ( + crashReportPrefix = "core_crash_" + crashReportSuffix = ".log" + maxCrashReports = 10 +) + +// writeCrashReport persists a captured xray crash chunk to the log folder +// with nanosecond-precision filename so restart-loop bursts don't overwrite +// each other, and prunes old reports to keep the folder bounded. func writeCrashReport(m []byte) error { - crashReportPath := config.GetBinFolderPath() + "/core_crash_" + time.Now().Format("20060102_150405") + ".log" - return os.WriteFile(crashReportPath, m, 0644) + dir := config.GetLogFolder() + if err := os.MkdirAll(dir, 0o770); err != nil { + return err + } + pruneOldCrashReports(dir, maxCrashReports-1) + name := crashReportPrefix + time.Now().Format("20060102_150405_000000000") + crashReportSuffix + return os.WriteFile(filepath.Join(dir, name), m, 0o640) +} + +func pruneOldCrashReports(dir string, keep int) { + entries, err := os.ReadDir(dir) + if err != nil { + return + } + var reports []string + for _, e := range entries { + n := e.Name() + if !e.IsDir() && strings.HasPrefix(n, crashReportPrefix) && strings.HasSuffix(n, crashReportSuffix) { + reports = append(reports, n) + } + } + if len(reports) <= keep { + return + } + sort.Strings(reports) + for _, old := range reports[:len(reports)-keep] { + _ = os.Remove(filepath.Join(dir, old)) + } }