Publish current app and server
This commit is contained in:
@@ -56,15 +56,25 @@ type EventPage struct {
|
||||
}
|
||||
|
||||
// Buffer is a bounded, concurrency-safe ring of recent structured log records.
|
||||
// Bounding is important: a broken TV can generate traffic indefinitely, while 20,000
|
||||
// records is still enough context for an operator to inspect a sustained incident.
|
||||
// Bounding is important: a broken TV can generate traffic indefinitely. The configured
|
||||
// ring retains enough context for an operator without growing or shifting on every write.
|
||||
type Buffer struct {
|
||||
mu sync.RWMutex
|
||||
capacity int
|
||||
events []Event
|
||||
start int
|
||||
next atomic.Int64
|
||||
}
|
||||
|
||||
// ParseCapacity returns a non-negative log buffer capacity from configuration.
|
||||
func ParseCapacity(value string, fallback int) int {
|
||||
capacity, err := strconv.Atoi(strings.TrimSpace(value))
|
||||
if err != nil || capacity < 0 {
|
||||
return fallback
|
||||
}
|
||||
return capacity
|
||||
}
|
||||
|
||||
// NewBuffered writes normal text logs and mirrors accepted records into a ring buffer.
|
||||
func NewBuffered(w io.Writer, level slog.Leveler, capacity int) (*slog.Logger, *Buffer) {
|
||||
options := &slog.HandlerOptions{
|
||||
@@ -158,8 +168,8 @@ func (b *Buffer) append(event Event) {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
if len(b.events) == b.capacity {
|
||||
copy(b.events, b.events[1:])
|
||||
b.events[len(b.events)-1] = event
|
||||
b.events[b.start] = event
|
||||
b.start = (b.start + 1) % b.capacity
|
||||
return
|
||||
}
|
||||
b.events = append(b.events, event)
|
||||
@@ -182,20 +192,23 @@ func (b *Buffer) Events(after int64, limit int) EventPage {
|
||||
if len(b.events) == 0 {
|
||||
return page
|
||||
}
|
||||
page.Oldest = b.events[0].Sequence
|
||||
page.Oldest = b.events[b.start].Sequence
|
||||
if after < page.Oldest-1 {
|
||||
page.Dropped = page.Oldest - after - 1
|
||||
after = page.Oldest - 1
|
||||
}
|
||||
start := len(b.events)
|
||||
for i := range b.events {
|
||||
if b.events[i].Sequence > after {
|
||||
event := b.events[(b.start+i)%len(b.events)]
|
||||
if event.Sequence > after {
|
||||
start = i
|
||||
break
|
||||
}
|
||||
}
|
||||
end := min(start+limit, len(b.events))
|
||||
page.Events = append(page.Events, b.events[start:end]...)
|
||||
for i := start; i < end; i++ {
|
||||
page.Events = append(page.Events, b.events[(b.start+i)%len(b.events)])
|
||||
}
|
||||
if len(page.Events) > 0 {
|
||||
page.Next = page.Events[len(page.Events)-1].Sequence
|
||||
}
|
||||
|
||||
@@ -56,3 +56,12 @@ func TestParseLevel(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseCapacity(t *testing.T) {
|
||||
if got := ParseCapacity("1000", 5); got != 1000 {
|
||||
t.Fatalf("capacity = %d", got)
|
||||
}
|
||||
if got := ParseCapacity("-1", 5); got != 5 {
|
||||
t.Fatalf("negative capacity = %d", got)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user