diff --git a/web/backend/api/gateway.go b/web/backend/api/gateway.go index 201000ff3..90a31b2ff 100644 --- a/web/backend/api/gateway.go +++ b/web/backend/api/gateway.go @@ -762,7 +762,12 @@ func (h *Handler) startGatewayLocked(initialStatus string, existingPid int) (int // Wait for exit in background and clean up go func() { if err := cmd.Wait(); err != nil { - logger.ErrorC("gateway", fmt.Sprintf("Gateway process exited: %v", err)) + tail := gateway.logs.Tail(50) + if len(tail) > 0 { + logger.ErrorC("gateway", fmt.Sprintf("Gateway process exited: %v\nLast gateway logs:\n%s", err, strings.Join(tail, "\n"))) + } else { + logger.ErrorC("gateway", fmt.Sprintf("Gateway process exited: %v", err)) + } } else { logger.InfoC("gateway", "Gateway process exited normally") } diff --git a/web/backend/api/log.go b/web/backend/api/log.go index f83f6f34c..8ebb60384 100644 --- a/web/backend/api/log.go +++ b/web/backend/api/log.go @@ -95,3 +95,35 @@ func (b *LogBuffer) RunID() int { return b.runID } + +// Tail returns up to the last n buffered lines (newest last). +// If n <= 0, it returns nil. +func (b *LogBuffer) Tail(n int) []string { + if n <= 0 { + return nil + } + + b.mu.RLock() + defer b.mu.RUnlock() + + if b.total == 0 || len(b.lines) == 0 { + return nil + } + + if n > len(b.lines) { + n = len(b.lines) + } + + result := make([]string, n) + + if b.total <= b.cap { + copy(result, b.lines[len(b.lines)-n:]) + return result + } + + start := (b.total - n) % b.cap + for i := 0; i < n; i++ { + result[i] = b.lines[(start+i)%b.cap] + } + return result +}