93 lines
2.0 KiB
Go
93 lines
2.0 KiB
Go
package logger
|
|
|
|
import (
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"douyu_go/internal/constants"
|
|
"douyu_go/internal/types"
|
|
)
|
|
|
|
// MemoryLogStore 内存日志缓冲,实现 io.Writer 接口。
|
|
type MemoryLogStore struct {
|
|
mu sync.RWMutex
|
|
nextID int64
|
|
max int
|
|
filePath string
|
|
entries []types.LogEntry
|
|
}
|
|
|
|
// NewMemoryLogStore 创建内存日志缓冲,最多保留 max 条最新日志。
|
|
func NewMemoryLogStore(max int) *MemoryLogStore {
|
|
if max <= 0 {
|
|
max = 500
|
|
}
|
|
return &MemoryLogStore{
|
|
max: max,
|
|
entries: make([]types.LogEntry, 0, max),
|
|
}
|
|
}
|
|
|
|
// Write 实现 io.Writer,把标准 log 输出转存为内存日志。
|
|
func (s *MemoryLogStore) Write(p []byte) (int, error) {
|
|
text := strings.TrimRight(string(p), "\r\n")
|
|
if text == "" {
|
|
return len(p), nil
|
|
}
|
|
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
for _, line := range strings.Split(text, "\n") {
|
|
line = strings.TrimRight(line, "\r")
|
|
if strings.TrimSpace(line) == "" {
|
|
continue
|
|
}
|
|
category, level := ParseLogMeta(line)
|
|
if level == "" {
|
|
level = DetectLogLevel(line)
|
|
}
|
|
s.nextID++
|
|
s.entries = append(s.entries, types.LogEntry{
|
|
ID: s.nextID,
|
|
Time: time.Now().In(constants.CST).Format("2006-01-02 15:04:05.000"),
|
|
Level: level,
|
|
Category: category,
|
|
Message: line,
|
|
})
|
|
if len(s.entries) > s.max {
|
|
copy(s.entries, s.entries[len(s.entries)-s.max:])
|
|
s.entries = s.entries[:s.max]
|
|
}
|
|
}
|
|
return len(p), nil
|
|
}
|
|
|
|
// SetFilePath 保存当前日志文件路径。
|
|
func (s *MemoryLogStore) SetFilePath(path string) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
s.filePath = path
|
|
}
|
|
|
|
// Since 返回 ID 大于 since 的日志。
|
|
func (s *MemoryLogStore) Since(since int64) []types.LogEntry {
|
|
s.mu.RLock()
|
|
defer s.mu.RUnlock()
|
|
|
|
out := make([]types.LogEntry, 0, len(s.entries))
|
|
for _, entry := range s.entries {
|
|
if entry.ID > since {
|
|
out = append(out, entry)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
// FilePath 返回当前日志文件路径。
|
|
func (s *MemoryLogStore) FilePath() string {
|
|
s.mu.RLock()
|
|
defer s.mu.RUnlock()
|
|
return s.filePath
|
|
}
|