mirror of
https://github.com/ctwj/urldb.git
synced 2025-11-25 03:15:04 +08:00
255 lines
6.7 KiB
Go
255 lines
6.7 KiB
Go
package utils
|
||
|
||
import (
|
||
"bufio"
|
||
"fmt"
|
||
"os"
|
||
"path/filepath"
|
||
"regexp"
|
||
"sort"
|
||
"strings"
|
||
"time"
|
||
)
|
||
|
||
// TelegramLogEntry Telegram日志条目
|
||
type TelegramLogEntry struct {
|
||
Timestamp time.Time `json:"timestamp"`
|
||
Level string `json:"level"`
|
||
Message string `json:"message"`
|
||
File string `json:"file,omitempty"`
|
||
Line int `json:"line,omitempty"`
|
||
Category string `json:"category,omitempty"` // telegram, push, message等
|
||
}
|
||
|
||
// GetTelegramLogs 获取Telegram相关的日志
|
||
func GetTelegramLogs(startTime *time.Time, endTime *time.Time, limit int) ([]TelegramLogEntry, error) {
|
||
logDir := "logs"
|
||
if _, err := os.Stat(logDir); os.IsNotExist(err) {
|
||
return []TelegramLogEntry{}, nil
|
||
}
|
||
|
||
// 查找所有日志文件,包括当前的app.log和历史日志文件
|
||
allFiles, err := filepath.Glob(filepath.Join(logDir, "*.log"))
|
||
if err != nil {
|
||
return nil, fmt.Errorf("查找日志文件失败: %v", err)
|
||
}
|
||
|
||
if len(allFiles) == 0 {
|
||
return []TelegramLogEntry{}, nil
|
||
}
|
||
|
||
// 将app.log放在最前面,其他文件按时间排序
|
||
var files []string
|
||
var otherFiles []string
|
||
|
||
for _, file := range allFiles {
|
||
if filepath.Base(file) == "app.log" {
|
||
files = append(files, file) // 当前日志文件优先
|
||
} else {
|
||
otherFiles = append(otherFiles, file)
|
||
}
|
||
}
|
||
|
||
// 其他文件按时间排序,最近的在前面
|
||
sort.Sort(sort.Reverse(sort.StringSlice(otherFiles)))
|
||
files = append(files, otherFiles...)
|
||
|
||
// files现在已经是app.log优先,然后是其他文件按时间倒序排列
|
||
|
||
var allEntries []TelegramLogEntry
|
||
|
||
// 编译Telegram相关的正则表达式
|
||
telegramRegex := regexp.MustCompile(`(?i)(\[TELEGRAM.*?\])`)
|
||
// 修正正则表达式以匹配实际的日志格式: 2025/01/20 14:30:15 [INFO] [file:line] [TELEGRAM] message
|
||
messageRegex := regexp.MustCompile(`(\d{4}/\d{2}/\d{2}\s+\d{2}:\d{2}:\d{2})\s+\[(\w+)\]\s+\[.*?:\d+\]\s+\[TELEGRAM.*?\]\s+(.*)`)
|
||
|
||
for _, file := range files {
|
||
entries, err := parseTelegramLogsFromFile(file, telegramRegex, messageRegex, startTime, endTime)
|
||
if err != nil {
|
||
continue // 跳过读取失败的文件
|
||
}
|
||
|
||
allEntries = append(allEntries, entries...)
|
||
|
||
// 如果已经达到限制数量,退出
|
||
if limit > 0 && len(allEntries) >= limit {
|
||
break
|
||
}
|
||
}
|
||
|
||
// 按时间排序,最新的在前面
|
||
sort.Slice(allEntries, func(i, j int) bool {
|
||
return allEntries[i].Timestamp.After(allEntries[j].Timestamp)
|
||
})
|
||
|
||
// 限制返回数量
|
||
if limit > 0 && len(allEntries) > limit {
|
||
allEntries = allEntries[:limit]
|
||
}
|
||
|
||
// 分类日志
|
||
for i := range allEntries {
|
||
allEntries[i].Category = categorizeLog(allEntries[i].Message)
|
||
}
|
||
|
||
return allEntries, nil
|
||
}
|
||
|
||
// parseTelegramLogsFromFile 解析单个日志文件中的Telegram日志
|
||
func parseTelegramLogsFromFile(filePath string, telegramRegex, messageRegex *regexp.Regexp, startTime, endTime *time.Time) ([]TelegramLogEntry, error) {
|
||
file, err := os.Open(filePath)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
defer file.Close()
|
||
|
||
var entries []TelegramLogEntry
|
||
scanner := bufio.NewScanner(file)
|
||
|
||
for scanner.Scan() {
|
||
line := scanner.Text()
|
||
|
||
// 检查是否是Telegram相关日志
|
||
if !telegramRegex.MatchString(line) {
|
||
continue
|
||
}
|
||
|
||
// 解析日志行
|
||
entry, err := parseLogLine(line, messageRegex)
|
||
if err != nil {
|
||
continue
|
||
}
|
||
|
||
// 时间过滤
|
||
if startTime != nil && entry.Timestamp.Before(*startTime) {
|
||
continue
|
||
}
|
||
if endTime != nil && entry.Timestamp.After(*endTime) {
|
||
continue
|
||
}
|
||
|
||
entries = append(entries, entry)
|
||
}
|
||
|
||
return entries, scanner.Err()
|
||
}
|
||
|
||
// parseLogLine 解析单行日志
|
||
func parseLogLine(line string, messageRegex *regexp.Regexp) (TelegramLogEntry, error) {
|
||
// 匹配日志格式: 2006/01/02 15:04:05 [LEVEL] [file:line] [TELEGRAM] message
|
||
matches := messageRegex.FindStringSubmatch(line)
|
||
if len(matches) < 4 {
|
||
return TelegramLogEntry{}, fmt.Errorf("无法解析日志行: %s", line)
|
||
}
|
||
|
||
timeStr := matches[1]
|
||
level := matches[2]
|
||
message := matches[3]
|
||
|
||
// 解析时间(使用本地时区)
|
||
location, err := time.LoadLocation("Asia/Shanghai")
|
||
if err != nil {
|
||
return TelegramLogEntry{}, fmt.Errorf("加载时区失败: %v", err)
|
||
}
|
||
|
||
timestamp, err := time.ParseInLocation("2006/01/02 15:04:05", timeStr, location)
|
||
if err != nil {
|
||
return TelegramLogEntry{}, fmt.Errorf("时间解析失败: %v", err)
|
||
}
|
||
|
||
return TelegramLogEntry{
|
||
Timestamp: timestamp,
|
||
Level: level,
|
||
Message: message,
|
||
}, nil
|
||
}
|
||
|
||
// categorizeLog 对日志进行分类
|
||
func categorizeLog(message string) string {
|
||
message = strings.ToLower(message)
|
||
|
||
switch {
|
||
case strings.Contains(message, "推送") || strings.Contains(message, "push"):
|
||
return "push"
|
||
case strings.Contains(message, "消息") || strings.Contains(message, "message") || strings.Contains(message, "收到"):
|
||
return "message"
|
||
case strings.Contains(message, "频道") || strings.Contains(message, "群组") || strings.Contains(message, "register"):
|
||
return "channel"
|
||
case strings.Contains(message, "启动") || strings.Contains(message, "停止") || strings.Contains(message, "start") || strings.Contains(message, "stop"):
|
||
return "service"
|
||
default:
|
||
return "general"
|
||
}
|
||
}
|
||
|
||
// GetTelegramLogStats 获取Telegram日志统计
|
||
func GetTelegramLogStats(hours int) (map[string]interface{}, error) {
|
||
endTime := time.Now()
|
||
startTime := endTime.Add(-time.Duration(hours) * time.Hour)
|
||
|
||
entries, err := GetTelegramLogs(&startTime, &endTime, 0)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
stats := map[string]interface{}{
|
||
"total_logs": len(entries),
|
||
"categories": make(map[string]int),
|
||
"levels": make(map[string]int),
|
||
"timeline": make(map[string]int), // 按小时统计
|
||
}
|
||
|
||
categoryStats := stats["categories"].(map[string]int)
|
||
levelStats := stats["levels"].(map[string]int)
|
||
timelineStats := stats["timeline"].(map[string]int)
|
||
|
||
for _, entry := range entries {
|
||
// 分类统计
|
||
categoryStats[entry.Category]++
|
||
|
||
// 级别统计
|
||
levelStats[entry.Level]++
|
||
|
||
// 时间线统计(按小时)
|
||
hourKey := entry.Timestamp.Format("2006-01-02 15:00")
|
||
timelineStats[hourKey]++
|
||
}
|
||
|
||
return stats, nil
|
||
}
|
||
|
||
// ClearOldTelegramLogs 清理旧的Telegram日志(保留最近N天的日志)
|
||
func ClearOldTelegramLogs(daysToKeep int) error {
|
||
logDir := "logs"
|
||
if _, err := os.Stat(logDir); os.IsNotExist(err) {
|
||
return nil // 日志目录不存在,无需清理
|
||
}
|
||
|
||
files, err := filepath.Glob(filepath.Join(logDir, "*.log"))
|
||
if err != nil {
|
||
return fmt.Errorf("查找日志文件失败: %v", err)
|
||
}
|
||
|
||
cutoffTime := time.Now().AddDate(0, 0, -daysToKeep)
|
||
|
||
deletedCount := 0
|
||
for _, file := range files {
|
||
fileInfo, err := os.Stat(file)
|
||
if err != nil {
|
||
continue
|
||
}
|
||
|
||
if fileInfo.ModTime().Before(cutoffTime) {
|
||
if err := os.Remove(file); err == nil {
|
||
deletedCount++
|
||
}
|
||
}
|
||
}
|
||
|
||
if deletedCount > 0 {
|
||
Info("清理了 %d 个旧的日志文件", deletedCount)
|
||
}
|
||
|
||
return nil
|
||
}
|