fix(xray): capture whole core crash reports instead of the first pipe chunk

LogWriter inspected each pipe chunk on its own. The Go runtime writes
"panic: " with a separate write before the message and traceback, so the
crash report was written from that first chunk alone and the panel left
7-byte core_crash_*.log files holding only "panic: ". Any ordinary log
line mentioning panic/exception/stack trace was also treated as a crash
and dropped from the panel log.

The writer now splits output into lines, starts a crash only on a line
beginning with the Go runtime's "panic: " / "fatal error: " header, keeps
everything after it, and writes one report from Flush, which
waitForCommand calls after cmd.Wait has delivered all output. The header
line becomes LastLine so GetResult shows the cause.

The report filename format "_000000000" is not a Go fractional-seconds
layout and always produced literal zeros, so reports from the same second
overwrote each other; os.CreateTemp now gives every report a unique name.
This commit is contained in:
MHSanaei
2026-10-14 15:31:09 +02:00
parent c45ffcdafa
commit 760f428c41
4 changed files with 242 additions and 84 deletions
+16 -5
View File
@@ -734,6 +734,7 @@ func (p *process) waitForCommand(cmd *exec.Cmd, done chan struct{}) {
defer close(done)
err := cmd.Wait()
p.logWriter.Flush()
if err == nil || p.intentionalStop.Load() {
return
}
@@ -830,17 +831,27 @@ const (
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.
// writeCrashReport persists a captured xray crash to the log folder; CreateTemp's
// random suffix keeps restart-loop bursts within one second from overwriting each other.
func writeCrashReport(m []byte) error {
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)
pattern := crashReportPrefix + time.Now().Format("20060102_150405") + "_*" + crashReportSuffix
f, err := os.CreateTemp(dir, pattern)
if err != nil {
return err
}
_, err = f.Write(m)
if cerr := f.Close(); err == nil {
err = cerr
}
if err == nil {
err = os.Chmod(f.Name(), 0o640)
}
return err
}
func pruneOldCrashReports(dir string, keep int) {