Commit3d575300Recorded29 Jun 2026Repositorycourier

Append to courier.log across restarts instead of truncating

Message

configure-persistent-log! rotated courier.log to .prev and opened a fresh (truncating) log on each leader start, keeping only ONE prior generation. A hang -> /mcp -> still-hung -> /mcp loop overwrote .prev and lost the original (load-bearing) hang evidence.

Switch to append mode so the whole history accumulates in one file. sigil has no O_APPEND file-open primitive (open-output-file always truncates), so append is emulated: carry the prior log content forward (capped at 2 MB) when reopening, separated by a restart marker.

Changed
 src/courier/main.sgl | 48 ++++++++++++++++++++++++++++++++++++------------
 1 file changed, 36 insertions(+), 12 deletions(-)
Diff
src/courier/main.sglmodified
@@ -8,6 +8,7 @@
8
(define-library (courier main)
9
(import (sigil core)
10
(sigil string)
+11
(sigil io)
12
(sigil env)
13
(sigil async)
14
(sigil fs)
@@ -51,21 +52,44 @@
52
;; Crash Logging
53
;; ============================================================
54
54
;; Configure persistent logging for leader mode.
55
;; Rotates the previous log to .prev and opens a fresh log file.
56
;; This ensures crash logs from the prior session survive.
+55
;; Max bytes of prior log carried forward when reopening. Bounds
+56
;; the file so append mode cannot grow without limit.
+57
(define *log-carryover-max* (* 2 1024 1024))
+58
+59
;; Open courier.log in append mode. sigil has no O_APPEND
+60
;; file-open primitive (open-output-file always truncates), so
+61
;; append is emulated: read the existing log (keeping only the
+62
;; last *log-carryover-max* bytes), reopen it (which truncates),
+63
;; and write the carried-over content back before logging
+64
;; resumes. The port is left positioned at end for the log module
+65
;; to append into.
+66
(define (open-log-append! log-path)
+67
(let ((prior (if (file-exists? log-path)
+68
(guard (e (else "")) (read-file-string log-path))
+69
"")))
+70
(let* ((n (string-length prior))
+71
(tail (if (> n *log-carryover-max*)
+72
(substring prior (- n *log-carryover-max*) n)
+73
prior))
+74
(port (open-output-file log-path)))
+75
(when (> (string-length tail) 0)
+76
(display tail port)
+77
(display "\n--- courier restart ---\n" port)
+78
(flush-output-port port))
+79
port)))
+80
+81
;; Configure persistent logging for leader mode in APPEND mode, so
+82
;; a hung instance's evidence survives across leader restarts. A
+83
;; /mcp reconnect SIGKILLs the leader and spawns a fresh one; the
+84
;; old rotate-current-to-.prev scheme kept only ONE prior
+85
;; generation, so a hang -> /mcp -> still-hung -> /mcp loop lost
+86
;; the original (load-bearing) evidence. Appending into one file
+87
;; keeps the whole history (capped by *log-carryover-max*).
88
(define (configure-persistent-log!)
89
(let* ((log-dir (path-dirname (default-relay-dir)))
59
(log-path (path-join log-dir "courier.log"))
60
(prev-path (path-join log-dir "courier.log.prev")))
+90
(log-path (path-join log-dir "courier.log")))
91
(ensure-directory log-dir)
62
;; Rotate: current -> prev (preserves last session's crash logs)
63
(when (file-exists? log-path)
64
(guard (e (else #f))
65
(when (file-exists? prev-path)
66
(delete-file prev-path))
67
(rename-file log-path prev-path)))
68
(log-configure! target: log-path)))
+92
(log-configure! target: (open-log-append! log-path))))
93
94
;; ============================================================
95
;; Entry Point