Add logging service
New Go program (logger/) that: - Accepts POSTed JSON log messages via POST /log - Stores last N messages in a ring buffer (default 1M) - Retrieves logs via GET /logs with limit/before/after filters - Shows status via GET /status Also updates express/logging.ts to POST messages to the logger service. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
126
logger/store.go
Normal file
126
logger/store.go
Normal file
@@ -0,0 +1,126 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Message represents a log entry from the express backend
|
||||
type Message struct {
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
Source string `json:"source"` // "logging" | "diagnostic" | "user"
|
||||
Text []string `json:"text"`
|
||||
}
|
||||
|
||||
// LogStore is a thread-safe ring buffer for log messages
|
||||
type LogStore struct {
|
||||
mu sync.RWMutex
|
||||
messages []Message
|
||||
head int // next write position
|
||||
full bool // whether buffer has wrapped
|
||||
capacity int
|
||||
}
|
||||
|
||||
// NewLogStore creates a new log store with the given capacity
|
||||
func NewLogStore(capacity int) *LogStore {
|
||||
return &LogStore{
|
||||
messages: make([]Message, capacity),
|
||||
capacity: capacity,
|
||||
}
|
||||
}
|
||||
|
||||
// Add inserts a new message into the store
|
||||
func (s *LogStore) Add(msg Message) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
s.messages[s.head] = msg
|
||||
s.head++
|
||||
if s.head >= s.capacity {
|
||||
s.head = 0
|
||||
s.full = true
|
||||
}
|
||||
}
|
||||
|
||||
// Count returns the number of messages in the store
|
||||
func (s *LogStore) Count() int {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
if s.full {
|
||||
return s.capacity
|
||||
}
|
||||
return s.head
|
||||
}
|
||||
|
||||
// GetRecent returns the most recent n messages, newest first
|
||||
func (s *LogStore) GetRecent(n int) []Message {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
count := s.Count()
|
||||
if n > count {
|
||||
n = count
|
||||
}
|
||||
if n == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
result := make([]Message, n)
|
||||
pos := s.head - 1
|
||||
for i := 0; i < n; i++ {
|
||||
if pos < 0 {
|
||||
pos = s.capacity - 1
|
||||
}
|
||||
result[i] = s.messages[pos]
|
||||
pos--
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Filter parameters for retrieving logs
|
||||
type FilterParams struct {
|
||||
Limit int // max messages to return (0 = default 100)
|
||||
Before int64 // only messages before this timestamp
|
||||
After int64 // only messages after this timestamp
|
||||
}
|
||||
|
||||
// GetFiltered returns messages matching the filter criteria
|
||||
func (s *LogStore) GetFiltered(params FilterParams) []Message {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
limit := params.Limit
|
||||
if limit <= 0 {
|
||||
limit = 100
|
||||
}
|
||||
|
||||
count := s.Count()
|
||||
if count == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
result := make([]Message, 0, limit)
|
||||
pos := s.head - 1
|
||||
|
||||
for i := 0; i < count && len(result) < limit; i++ {
|
||||
if pos < 0 {
|
||||
pos = s.capacity - 1
|
||||
}
|
||||
msg := s.messages[pos]
|
||||
|
||||
// Apply filters
|
||||
if params.Before > 0 && msg.Timestamp >= params.Before {
|
||||
pos--
|
||||
continue
|
||||
}
|
||||
if params.After > 0 && msg.Timestamp <= params.After {
|
||||
pos--
|
||||
continue
|
||||
}
|
||||
|
||||
result = append(result, msg)
|
||||
pos--
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
Reference in New Issue
Block a user