旧链路:LogEvent→log.Print→logFileWriter→MemoryLogStore.Write 再用 ParseLogLine 字符串切割把分类/级别/账号反解析回来,格式一调就崩。 新链路:LogEvent 直构 LogEntry(Time/Level/Category/Account 直接填值), 经 sink 分发到内存(同步)/文件(异步)/终端。Message 仍用 formatLogLine 渲染保持文件文本格式与前端清理逻辑兼容。 - MemoryLogStore 新增 Append(entry) 替代 Write 的字符串解析路径 - 删除 logFileWriter,新增 sink 统一管理三路输出 - 删除 log.SetOutput/log.Print 依赖 - 保留 ParseLogLine/DetectLogLevel/formatLogLine(AsyncLogWriter 丢弃 统计与测试仍用),但 log_store 不再调用 - 新增 TestStructuredLogging 覆盖 LogEvent→sink→Append 链路与 minLevel 过滤
71 lines
1.5 KiB
Go
71 lines
1.5 KiB
Go
package logger
|
|
|
|
import (
|
|
"sync"
|
|
|
|
"douyu_go/internal/types"
|
|
)
|
|
|
|
// MemoryLogStore 内存日志缓冲,供前端增量拉取。
|
|
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),
|
|
}
|
|
}
|
|
|
|
// Append 追加一条结构化日志,由内部分配自增 ID。
|
|
// 超过容量时丢弃最旧的条目,保留最近的 max 条。
|
|
func (s *MemoryLogStore) Append(entry types.LogEntry) types.LogEntry {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
s.nextID++
|
|
entry.ID = s.nextID
|
|
s.entries = append(s.entries, entry)
|
|
if len(s.entries) > s.max {
|
|
copy(s.entries, s.entries[len(s.entries)-s.max:])
|
|
s.entries = s.entries[:s.max]
|
|
}
|
|
return entry
|
|
}
|
|
|
|
// 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
|
|
}
|