Files
codexautopool/backend/internal/logger/logger.go

264 lines
5.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package logger
import (
"fmt"
"strconv"
"strings"
"sync"
"time"
)
// LogEntry 日志条目
type LogEntry struct {
Timestamp time.Time `json:"timestamp"`
Level string `json:"level"`
Message string `json:"message"`
Email string `json:"email,omitempty"`
Module string `json:"module,omitempty"`
}
// 日志存储
var (
logs = make([]LogEntry, 0, 1000)
logsMu sync.RWMutex
listeners = make(map[string]chan LogEntry)
listMu sync.RWMutex
)
// AddListener 添加日志监听器
func AddListener(id string) chan LogEntry {
listMu.Lock()
defer listMu.Unlock()
ch := make(chan LogEntry, 100)
listeners[id] = ch
return ch
}
// RemoveListener 移除日志监听器
func RemoveListener(id string) {
listMu.Lock()
defer listMu.Unlock()
if ch, ok := listeners[id]; ok {
close(ch)
delete(listeners, id)
}
}
// broadcast 广播日志
func broadcast(entry LogEntry) {
listMu.RLock()
defer listMu.RUnlock()
for _, ch := range listeners {
select {
case ch <- entry:
default:
}
}
}
// log 记录日志
func log(level, message, email, module string) {
entry := LogEntry{
Timestamp: time.Now(),
Level: level,
Message: message,
Email: email,
Module: module,
}
logsMu.Lock()
if len(logs) >= 1000 {
logs = logs[100:]
}
logs = append(logs, entry)
logsMu.Unlock()
broadcast(entry)
// 打印到控制台 (带时间戳和颜色)
timestamp := entry.Timestamp.Format("15:04:05")
// ANSI 颜色代码
colorReset := "\033[0m"
colorGray := "\033[90m"
colorGreen := "\033[32m"
colorRed := "\033[31m"
colorYellow := "\033[33m"
colorCyan := "\033[36m"
// Team 颜色列表(用于区分不同 Team
teamColors := []string{
"\033[38;5;39m", // 亮蓝
"\033[38;5;208m", // 橙色
"\033[38;5;141m", // 紫色
"\033[38;5;48m", // 青绿
"\033[38;5;197m", // 粉红
"\033[38;5;226m", // 亮黄
"\033[38;5;87m", // 青色
"\033[38;5;156m", // 浅绿
"\033[38;5;219m", // 浅粉
"\033[38;5;117m", // 天蓝
}
// 从消息中提取 Team 编号
teamColor := ""
if strings.Contains(message, "[Team ") {
start := strings.Index(message, "[Team ")
if start >= 0 {
end := strings.Index(message[start:], "]")
if end > 0 {
teamStr := message[start+6 : start+end]
if teamNum, err := strconv.Atoi(teamStr); err == nil && teamNum > 0 {
teamColor = teamColors[(teamNum-1)%len(teamColors)]
}
}
}
}
prefix := ""
color := ""
switch level {
case "info":
prefix = "INFO "
color = colorCyan
case "success":
prefix = "SUCCESS"
color = colorGreen
case "error":
prefix = "ERROR "
color = colorRed
case "warning":
prefix = "WARN "
color = colorYellow
}
// 模块名固定宽度8字符
moduleStr := module
if len(moduleStr) < 8 {
moduleStr = moduleStr + strings.Repeat(" ", 8-len(moduleStr))
} else if len(moduleStr) > 8 {
moduleStr = moduleStr[:8]
}
// 如果是 Team 相关日志,消息使用 Team 颜色
msgColor := colorReset
if teamColor != "" {
msgColor = teamColor
}
if email != "" {
// 截断长邮箱,保持对齐
emailDisplay := email
if len(emailDisplay) > 35 {
emailDisplay = emailDisplay[:32] + "..."
}
fmt.Printf("%s%s%s %s[%s]%s [%s] %s%s%s\n",
colorGray, timestamp, colorReset,
color, prefix, colorReset,
moduleStr, msgColor, message, colorReset)
} else {
fmt.Printf("%s%s%s %s[%s]%s [%s] %s%s%s\n",
colorGray, timestamp, colorReset,
color, prefix, colorReset,
moduleStr, msgColor, message, colorReset)
}
}
// Info 记录信息日志
func Info(message, email, module string) {
log("info", message, email, module)
}
// Success 记录成功日志
func Success(message, email, module string) {
log("success", message, email, module)
}
// Error 记录错误日志
func Error(message, email, module string) {
log("error", message, email, module)
}
// Warning 记录警告日志
func Warning(message, email, module string) {
log("warning", message, email, module)
}
// GetLogs 获取日志
func GetLogs(limit int) []LogEntry {
logsMu.RLock()
defer logsMu.RUnlock()
if limit <= 0 || limit > len(logs) {
limit = len(logs)
}
start := len(logs) - limit
if start < 0 {
start = 0
}
result := make([]LogEntry, limit)
copy(result, logs[start:])
return result
}
// GetLogsByModule 按模块筛选日志并分页(最新的在前)
func GetLogsByModule(module string, page, pageSize int) ([]LogEntry, int) {
logsMu.RLock()
defer logsMu.RUnlock()
// 倒序收集匹配的日志
var filtered []LogEntry
for i := len(logs) - 1; i >= 0; i-- {
if logs[i].Module == module {
filtered = append(filtered, logs[i])
}
}
total := len(filtered)
if page < 1 {
page = 1
}
if pageSize <= 0 {
pageSize = 5
}
start := (page - 1) * pageSize
if start >= total {
return []LogEntry{}, total
}
end := start + pageSize
if end > total {
end = total
}
return filtered[start:end], total
}
// ClearLogs 清空日志
func ClearLogs() {
logsMu.Lock()
defer logsMu.Unlock()
logs = make([]LogEntry, 0, 1000)
}
// ClearLogsByModule 按模块清除日志
func ClearLogsByModule(module string) int {
logsMu.Lock()
defer logsMu.Unlock()
// 过滤掉指定模块的日志
var newLogs []LogEntry
cleared := 0
for _, log := range logs {
if log.Module != module {
newLogs = append(newLogs, log)
} else {
cleared++
}
}
logs = newLogs
return cleared
}