174 lines
3.7 KiB
Go
174 lines
3.7 KiB
Go
package api
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"sync"
|
|
"time"
|
|
|
|
"home-vue-go/internal/config"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
var logBuffer []string
|
|
var maxLogLines = 1000
|
|
var logBufferMutex sync.RWMutex
|
|
|
|
// 初始化日志缓冲区
|
|
func InitLogBuffer() {
|
|
logBufferMutex.Lock()
|
|
defer logBufferMutex.Unlock()
|
|
logBuffer = make([]string, 0, maxLogLines)
|
|
}
|
|
|
|
// 添加日志到缓冲区
|
|
func AddLogToBuffer(logLine string) {
|
|
logBufferMutex.Lock()
|
|
defer logBufferMutex.Unlock()
|
|
logBuffer = append(logBuffer, logLine)
|
|
if len(logBuffer) > maxLogLines {
|
|
logBuffer = logBuffer[len(logBuffer)-maxLogLines:]
|
|
}
|
|
}
|
|
|
|
// 获取后端日志
|
|
func GetBackendLogs(cfg *config.Config) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
lines := c.DefaultQuery("lines", "100")
|
|
linesInt := 100
|
|
if lines == "all" {
|
|
linesInt = maxLogLines
|
|
} else {
|
|
// 尝试解析lines参数
|
|
if n, err := parseIntLogs(lines); err == nil && n > 0 {
|
|
linesInt = n
|
|
if linesInt > maxLogLines {
|
|
linesInt = maxLogLines
|
|
}
|
|
}
|
|
}
|
|
|
|
// 从缓冲区获取日志
|
|
logBufferMutex.RLock()
|
|
logs := append([]string(nil), logBuffer...)
|
|
logBufferMutex.RUnlock()
|
|
if len(logs) > linesInt {
|
|
logs = logs[len(logs)-linesInt:]
|
|
}
|
|
|
|
// 尝试从日志文件读取(如果存在)
|
|
logFile := filepath.Join(cfg.DataDir, "app.log")
|
|
if fileLogs, err := readLogFile(logFile, linesInt); err == nil && len(fileLogs) > 0 {
|
|
// 合并文件日志和缓冲区日志
|
|
allLogs := append(fileLogs, logs...)
|
|
// 去重并排序
|
|
logs = deduplicateLogs(allLogs)
|
|
if len(logs) > linesInt {
|
|
logs = logs[len(logs)-linesInt:]
|
|
}
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"logs": logs,
|
|
"count": len(logs),
|
|
})
|
|
}
|
|
}
|
|
|
|
// 读取日志文件
|
|
func readLogFile(filePath string, maxLines int) ([]string, error) {
|
|
file, err := os.Open(filePath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer file.Close()
|
|
|
|
var lines []string
|
|
scanner := bufio.NewScanner(file)
|
|
for scanner.Scan() {
|
|
lines = append(lines, scanner.Text())
|
|
if len(lines) >= maxLines*2 {
|
|
// 只保留最后maxLines行
|
|
lines = lines[len(lines)-maxLines:]
|
|
}
|
|
}
|
|
|
|
if err := scanner.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// 返回最后maxLines行
|
|
if len(lines) > maxLines {
|
|
return lines[len(lines)-maxLines:], nil
|
|
}
|
|
return lines, nil
|
|
}
|
|
|
|
// 去重日志
|
|
func deduplicateLogs(logs []string) []string {
|
|
seen := make(map[string]bool)
|
|
result := make([]string, 0)
|
|
for i := len(logs) - 1; i >= 0; i-- {
|
|
if !seen[logs[i]] {
|
|
seen[logs[i]] = true
|
|
result = append([]string{logs[i]}, result...)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
// 简单的整数解析(logs.go专用)
|
|
func parseIntLogs(s string) (int, error) {
|
|
var n int
|
|
for _, char := range s {
|
|
if char >= '0' && char <= '9' {
|
|
n = n*10 + int(char-'0')
|
|
} else {
|
|
return 0, io.EOF
|
|
}
|
|
}
|
|
return n, nil
|
|
}
|
|
|
|
// 日志中间件 - 记录请求日志
|
|
func LoggingMiddleware() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
start := time.Now()
|
|
path := c.Request.URL.Path
|
|
query := c.Request.URL.RawQuery
|
|
ip := c.ClientIP()
|
|
|
|
c.Next()
|
|
|
|
latency := time.Since(start)
|
|
method := c.Request.Method
|
|
statusCode := c.Writer.Status()
|
|
|
|
logLine := formatLogLine(start, method, statusCode, latency, path, query, ip)
|
|
AddLogToBuffer(logLine)
|
|
}
|
|
}
|
|
|
|
func formatLogLine(timestamp time.Time, method string, statusCode int, latency time.Duration, path, query, ip string) string {
|
|
if query != "" {
|
|
path = path + "?" + query
|
|
}
|
|
// 使用Asia/Shanghai时区格式化时间
|
|
loc, _ := time.LoadLocation("Asia/Shanghai")
|
|
shanghaiTime := timestamp.In(loc)
|
|
// 格式化日志:只包含一个时间戳
|
|
return fmt.Sprintf("[%s] %s %s :: %s %d %s",
|
|
shanghaiTime.Format("2006-01-02 15:04:05"),
|
|
method,
|
|
path,
|
|
ip,
|
|
statusCode,
|
|
latency.String(),
|
|
)
|
|
}
|