fix(logging): keep cursors valid after rollover

This commit is contained in:
zarzet
2026-07-16 10:56:26 +07:00
parent 648f36244b
commit b7e96facbe
2 changed files with 81 additions and 13 deletions
@@ -57,6 +57,41 @@ func TestLogBufferExportedHelpersAndRedaction(t *testing.T) {
}
}
func TestLogBufferCursorSurvivesRollover(t *testing.T) {
lb := &LogBuffer{
entries: make([]LogEntry, 3),
maxSize: 3,
loggingEnabled: true,
}
for _, message := range []string{"one", "two", "three"} {
lb.Add("INFO", "Test", message)
}
initial, cursor := lb.getSince(0)
if cursor != 3 || len(initial) != 3 || initial[0].Message != "one" {
t.Fatalf("initial logs/cursor = %#v/%d", initial, cursor)
}
lb.Add("INFO", "Test", "four")
newLogs, cursor := lb.getSince(cursor)
if cursor != 4 || len(newLogs) != 1 || newLogs[0].Message != "four" {
t.Fatalf("rollover logs/cursor = %#v/%d", newLogs, cursor)
}
lb.Add("INFO", "Test", "five")
lb.Add("INFO", "Test", "six")
retained, cursor := lb.getSince(1)
if cursor != 6 || len(retained) != 3 || retained[0].Message != "four" || retained[2].Message != "six" {
t.Fatalf("retained logs/cursor = %#v/%d", retained, cursor)
}
lb.Clear()
lb.Add("INFO", "Test", "seven")
afterClear, cursor := lb.getSince(6)
if cursor != 7 || len(afterClear) != 1 || afterClear[0].Message != "seven" {
t.Fatalf("after clear logs/cursor = %#v/%d", afterClear, cursor)
}
}
func TestProgressItemHelpersAndWriter(t *testing.T) {
ClearAllItemProgress()
itemID := "progress-writer"
+46 -13
View File
@@ -19,6 +19,9 @@ type LogEntry struct {
type LogBuffer struct {
entries []LogEntry
maxSize int
head int
count int
nextIndex int
mu sync.RWMutex
loggingEnabled bool
}
@@ -49,7 +52,7 @@ func sanitizeSensitiveLogText(message string) string {
func GetLogBuffer() *LogBuffer {
logBufferOnce.Do(func() {
globalLogBuffer = &LogBuffer{
entries: make([]LogEntry, 0, defaultLogBufferSize),
entries: make([]LogEntry, defaultLogBufferSize),
maxSize: defaultLogBufferSize,
loggingEnabled: false,
}
@@ -86,10 +89,23 @@ func (lb *LogBuffer) Add(level, tag, message string) {
Message: message,
}
if len(lb.entries) >= lb.maxSize {
lb.entries = lb.entries[1:]
if lb.maxSize <= 0 {
return
}
lb.entries = append(lb.entries, entry)
if len(lb.entries) != lb.maxSize {
lb.entries = make([]LogEntry, lb.maxSize)
lb.head = 0
lb.count = 0
}
if lb.count < lb.maxSize {
index := (lb.head + lb.count) % lb.maxSize
lb.entries[index] = entry
lb.count++
} else {
lb.entries[lb.head] = entry
lb.head = (lb.head + 1) % lb.maxSize
}
lb.nextIndex++
fmt.Printf("[%s] %s\n", tag, message)
}
@@ -98,7 +114,7 @@ func (lb *LogBuffer) GetAll() string {
lb.mu.RLock()
defer lb.mu.RUnlock()
jsonBytes, _ := json.Marshal(lb.entries)
jsonBytes, _ := json.Marshal(lb.snapshotLocked(0))
return string(jsonBytes)
}
@@ -106,27 +122,44 @@ func (lb *LogBuffer) getSince(index int) ([]LogEntry, int) {
lb.mu.RLock()
defer lb.mu.RUnlock()
if index < 0 {
index = 0
earliest := lb.nextIndex - lb.count
if index < earliest {
index = earliest
}
if index >= len(lb.entries) {
return []LogEntry{}, len(lb.entries)
if index >= lb.nextIndex {
return []LogEntry{}, lb.nextIndex
}
return lb.snapshotLocked(index - earliest), lb.nextIndex
}
entries := lb.entries[index:]
return entries, len(lb.entries)
func (lb *LogBuffer) snapshotLocked(offset int) []LogEntry {
if offset < 0 {
offset = 0
}
if offset >= lb.count {
return []LogEntry{}
}
result := make([]LogEntry, lb.count-offset)
for i := offset; i < lb.count; i++ {
result[i-offset] = lb.entries[(lb.head+i)%lb.maxSize]
}
return result
}
func (lb *LogBuffer) Clear() {
lb.mu.Lock()
defer lb.mu.Unlock()
lb.entries = lb.entries[:0]
for i := range lb.entries {
lb.entries[i] = LogEntry{}
}
lb.head = 0
lb.count = 0
}
func (lb *LogBuffer) Count() int {
lb.mu.RLock()
defer lb.mu.RUnlock()
return len(lb.entries)
return lb.count
}
func LogDebug(tag, format string, args ...any) {