|
| 1 | +package lib |
| 2 | + |
| 3 | +import ( |
| 4 | + "sync" |
| 5 | + "time" |
| 6 | + |
| 7 | + "github.com/sirupsen/logrus" |
| 8 | +) |
| 9 | + |
| 10 | +// LogEntry represents a single log entry. |
| 11 | +type LogEntry struct { |
| 12 | + Timestamp time.Time `json:"timestamp"` |
| 13 | + Level string `json:"level"` |
| 14 | + Message string `json:"message"` |
| 15 | + Fields logrus.Fields `json:"fields,omitempty"` |
| 16 | +} |
| 17 | + |
| 18 | +// LogBuffer is a thread-safe circular buffer for log entries. |
| 19 | +type LogBuffer struct { |
| 20 | + entries []LogEntry |
| 21 | + size int |
| 22 | + index int |
| 23 | + mutex sync.RWMutex |
| 24 | + full bool |
| 25 | +} |
| 26 | + |
| 27 | +// NewLogBuffer creates a new log buffer with specified size. |
| 28 | +func NewLogBuffer(size int) *LogBuffer { |
| 29 | + return &LogBuffer{ |
| 30 | + entries: make([]LogEntry, size), |
| 31 | + size: size, |
| 32 | + index: 0, |
| 33 | + full: false, |
| 34 | + } |
| 35 | +} |
| 36 | + |
| 37 | +// Add adds a new log entry to the buffer. |
| 38 | +func (lb *LogBuffer) Add(entry LogEntry) { |
| 39 | + lb.mutex.Lock() |
| 40 | + defer lb.mutex.Unlock() |
| 41 | + |
| 42 | + lb.entries[lb.index] = entry |
| 43 | + lb.index = (lb.index + 1) % lb.size |
| 44 | + |
| 45 | + if lb.index == 0 { |
| 46 | + lb.full = true |
| 47 | + } |
| 48 | +} |
| 49 | + |
| 50 | +// GetAll returns all log entries in chronological order. |
| 51 | +func (lb *LogBuffer) GetAll() []LogEntry { |
| 52 | + lb.mutex.RLock() |
| 53 | + defer lb.mutex.RUnlock() |
| 54 | + |
| 55 | + if !lb.full && lb.index == 0 { |
| 56 | + return []LogEntry{} |
| 57 | + } |
| 58 | + |
| 59 | + var result []LogEntry |
| 60 | + |
| 61 | + if lb.full { |
| 62 | + // Buffer is full, start from current index (oldest entry) |
| 63 | + result = make([]LogEntry, 0, lb.size) |
| 64 | + for i := 0; i < lb.size; i++ { |
| 65 | + idx := (lb.index + i) % lb.size |
| 66 | + result = append(result, lb.entries[idx]) |
| 67 | + } |
| 68 | + } else { |
| 69 | + // Buffer is not full, return entries from 0 to index |
| 70 | + result = make([]LogEntry, lb.index) |
| 71 | + copy(result, lb.entries[:lb.index]) |
| 72 | + } |
| 73 | + |
| 74 | + return result |
| 75 | +} |
| 76 | + |
| 77 | +// GetRecent returns the most recent n log entries. |
| 78 | +func (lb *LogBuffer) GetRecent(n int) []LogEntry { |
| 79 | + all := lb.GetAll() |
| 80 | + if len(all) <= n { |
| 81 | + return all |
| 82 | + } |
| 83 | + return all[len(all)-n:] |
| 84 | +} |
| 85 | + |
| 86 | +// Clear clears all log entries. |
| 87 | +func (lb *LogBuffer) Clear() { |
| 88 | + lb.mutex.Lock() |
| 89 | + defer lb.mutex.Unlock() |
| 90 | + |
| 91 | + lb.index = 0 |
| 92 | + lb.full = false |
| 93 | + lb.entries = make([]LogEntry, lb.size) |
| 94 | +} |
| 95 | + |
| 96 | +// Global log buffer instance. |
| 97 | +var globalLogBuffer *LogBuffer |
| 98 | + |
| 99 | +// InitLogBuffer initializes the global log buffer. |
| 100 | +func InitLogBuffer(size int) { |
| 101 | + globalLogBuffer = NewLogBuffer(size) |
| 102 | +} |
| 103 | + |
| 104 | +// GetLogBuffer returns the global log buffer instance. |
| 105 | +func GetLogBuffer() *LogBuffer { |
| 106 | + if globalLogBuffer == nil { |
| 107 | + InitLogBuffer(1000) // Default size |
| 108 | + } |
| 109 | + return globalLogBuffer |
| 110 | +} |
| 111 | + |
| 112 | +// LogHook is a logrus hook that captures logs in the buffer. |
| 113 | +type LogHook struct { |
| 114 | + buffer *LogBuffer |
| 115 | +} |
| 116 | + |
| 117 | +// NewLogHook creates a new log hook. |
| 118 | +func NewLogHook(buffer *LogBuffer) *LogHook { |
| 119 | + return &LogHook{ |
| 120 | + buffer: buffer, |
| 121 | + } |
| 122 | +} |
| 123 | + |
| 124 | +// Fire is called when a log event is fired. |
| 125 | +func (hook *LogHook) Fire(entry *logrus.Entry) error { |
| 126 | + logEntry := LogEntry{ |
| 127 | + Timestamp: entry.Time, |
| 128 | + Level: entry.Level.String(), |
| 129 | + Message: entry.Message, |
| 130 | + Fields: make(logrus.Fields), |
| 131 | + } |
| 132 | + |
| 133 | + // Copy fields |
| 134 | + for k, v := range entry.Data { |
| 135 | + logEntry.Fields[k] = v |
| 136 | + } |
| 137 | + |
| 138 | + hook.buffer.Add(logEntry) |
| 139 | + return nil |
| 140 | +} |
| 141 | + |
| 142 | +// Levels returns the available logging levels. |
| 143 | +func (hook *LogHook) Levels() []logrus.Level { |
| 144 | + return logrus.AllLevels |
| 145 | +} |
0 commit comments