livelog: annotate log truncation
Build log truncation is currently silent and can easily go unnoticed
unless you notice missing expected output (such as a filename line
following a function name in a stack trace).
Adjust livelog to explicitly annotate truncation at the end of a log so
that it is very clear to readers.
Fixes golang/go#45972
Change-Id: I5ad555232ba670bfb37d2eb3d2260307468c28a9
Reviewed-on: https://go-review.googlesource.com/c/build/+/317209
Trust: Michael Pratt <mpratt@google.com>
Run-TryBot: Michael Pratt <mpratt@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
diff --git a/livelog/livelog.go b/livelog/livelog.go
index c5b0c77..0f954af 100644
--- a/livelog/livelog.go
+++ b/livelog/livelog.go
@@ -11,15 +11,32 @@
"sync"
)
-const MaxBufferSize = 2 << 20 // 2MB of output is way more than we expect.
+const (
+ // MaxBufferSize is the maximum buffer size, as it is more output than
+ // we expect from reasonable tests.
+ MaxBufferSize = 2 << 20 // 2 MB
-// Buffer is a WriteCloser that provides multiple Readers that each yield the same data.
-// It is safe to Write to a Buffer while Readers consume that data.
-// Its zero value is a ready-to-use buffer.
+ // truncationMessage is added to the end of the log when it reaches the
+ // maximum size.
+ truncationMessage = "\n\n... log truncated ..."
+
+ // maxUserSize is the total user output we can place in the buffer
+ // while still leaving room for the truncation message.
+ maxUserSize = MaxBufferSize - len(truncationMessage)
+)
+
+// Buffer is an io.WriteCloser that provides multiple Readers that each yield
+// the same data.
+//
+// It is safe to Write to a Buffer while Readers consume data. A Buffer has a
+// maximum size of MaxBufferSize, after which Write will silently drop
+// additional data and the buffer will contain a truncation note at the end.
+//
+// The zero value is a ready-to-use buffer.
type Buffer struct {
mu sync.Mutex // Guards the fields below.
wake *sync.Cond // Created on demand by reader.
- buf []byte
+ buf []byte // Length is in the range [0, MaxBufferSize].
eof bool
lastID int
}
@@ -30,11 +47,20 @@
b.mu.Lock()
defer b.mu.Unlock()
+ needTrunc := false
b2len := len(b2)
- if len(b.buf)+b2len > MaxBufferSize {
- b2 = b2[:MaxBufferSize-len(b.buf)]
+ if len(b.buf) == MaxBufferSize {
+ // b.buf is full and truncationMessage was written.
+ b2 = nil
+ } else if len(b.buf)+b2len > maxUserSize {
+ b2 = b2[:maxUserSize-len(b.buf)]
+ needTrunc = true
+ // After this write, b.buf will reach MaxBufferSize length.
}
b.buf = append(b.buf, b2...)
+ if needTrunc {
+ b.buf = append(b.buf, []byte(truncationMessage)...)
+ }
b.wakeReaders()
return b2len, nil
}