From 4b698474b31530ada7ccad9ab6375591ad0e1b93 Mon Sep 17 00:00:00 2001 From: duorameng <2997944583@qq.com> Date: Tue, 28 Apr 2026 14:33:43 +0800 Subject: [PATCH] chore: add log size limit --- internal/constant/constant.go | 2 + internal/services/tasks/task_log_service.go | 11 ++-- internal/services/tasks/tiny_log.go | 29 +++++++++ internal/utils/buffer.go | 66 +++++++++++++++++++++ 4 files changed, 104 insertions(+), 4 deletions(-) create mode 100644 internal/utils/buffer.go diff --git a/internal/constant/constant.go b/internal/constant/constant.go index e91c82c..dc76c65 100644 --- a/internal/constant/constant.go +++ b/internal/constant/constant.go @@ -169,6 +169,8 @@ const ( PingPeriod = (PongWait * 9) / 10 // MaxMessageSize 允许的最大消息大小 MaxMessageSize = 1024 * 1024 // 1MB + // MaxLogSize 允许的最大日志大小 (保留末尾 10MB) + MaxLogSize = 10 * 1024 * 1024 // 10MB ) // TablePrefix 表前缀,从配置文件读取 diff --git a/internal/services/tasks/task_log_service.go b/internal/services/tasks/task_log_service.go index 750d3b1..765ce96 100644 --- a/internal/services/tasks/task_log_service.go +++ b/internal/services/tasks/task_log_service.go @@ -8,6 +8,7 @@ import ( "github.com/engigu/baihu-panel/internal/logger" "github.com/engigu/baihu-panel/internal/models" "github.com/engigu/baihu-panel/internal/systime" + "github.com/engigu/baihu-panel/internal/constant" "github.com/engigu/baihu-panel/internal/utils" ) @@ -161,8 +162,9 @@ func (s *TaskLogService) ProcessTaskCompletion(taskLog *models.TaskLog) error { // CreateTaskLogFromAgentResult 从 Agent 结果创建任务日志 func (s *TaskLogService) CreateTaskLogFromAgentResult(result *models.AgentTaskResult) (*models.TaskLog, error) { - // 压缩输出 - compressed, err := utils.CompressToBase64(result.Output) + // 裁剪并压缩输出 + trimmedOutput := utils.TrimLog(result.Output, constant.MaxLogSize) + compressed, err := utils.CompressToBase64(trimmedOutput) if err != nil { logger.Errorf("[TaskLog] 压缩日志失败: %v", err) compressed = "" @@ -202,8 +204,9 @@ func (s *TaskLogService) CreateTaskLogFromLocalExecution(taskID string, command, if isCompressed { compressed = output } else { - // 压缩输出 - compressed, err = utils.CompressToBase64(output) + // 裁剪并压缩输出 + trimmedOutput := utils.TrimLog(output, constant.MaxLogSize) + compressed, err = utils.CompressToBase64(trimmedOutput) if err != nil { logger.Errorf("[TaskLog] 压缩日志失败: %v", err) compressed = "" diff --git a/internal/services/tasks/tiny_log.go b/internal/services/tasks/tiny_log.go index 1a8e0f3..a381058 100644 --- a/internal/services/tasks/tiny_log.go +++ b/internal/services/tasks/tiny_log.go @@ -4,11 +4,13 @@ import ( "bufio" "bytes" "encoding/base64" + "fmt" "io" "os" "sync" "unicode/utf8" + "github.com/engigu/baihu-panel/internal/constant" "github.com/engigu/baihu-panel/internal/utils" ) @@ -236,6 +238,33 @@ func (l *TinyLog) CompressAndCleanup() (string, error) { zw := utils.GetZlibWriter(b64Writer) defer utils.PutZlibWriter(zw) + // 获取文件大小 + stat, err := f.Stat() + if err != nil { + return "", err + } + size := stat.Size() + maxSize := int64(constant.MaxLogSize) + if maxSize < 1024*1024 { + maxSize = 1024 * 1024 + } + + var readStart int64 = 0 + if size > maxSize { + readStart = size - maxSize + // 写入一条截断提示 + truncatedMsg := fmt.Sprintf("\n\n[System] 日志过长,已自动截断,仅保留末尾 %d MB...\n\n", maxSize/1024/1024) + if _, err := zw.Write([]byte(truncatedMsg)); err != nil { + return "", err + } + } + + if readStart > 0 { + if _, err := f.Seek(readStart, io.SeekStart); err != nil { + return "", err + } + } + // 流处理: 文件 -> Zlib -> Base64 -> 缓冲区 if _, err := io.Copy(zw, f); err != nil { return "", err diff --git a/internal/utils/buffer.go b/internal/utils/buffer.go new file mode 100644 index 0000000..8b10e59 --- /dev/null +++ b/internal/utils/buffer.go @@ -0,0 +1,66 @@ +package utils + +import ( + "fmt" +) + +// TailBuffer 是一个只保留最后 N 字节数据的缓冲区 +type TailBuffer struct { + limit int + data []byte + size int // 当前实际存储的大小 + pos int // 下一个写入位置 (针对环形缓冲区逻辑,但这里为了简单使用切片重排) +} + +// NewTailBuffer 创建一个限制大小为 limit 的尾部缓冲区 +func NewTailBuffer(limit int) *TailBuffer { + return &TailBuffer{ + limit: limit, + data: make([]byte, 0, limit), + } +} + +// Write 实现 io.Writer 接口 +func (b *TailBuffer) Write(p []byte) (n int, err error) { + n = len(p) + if n >= b.limit { + // 如果单次写入就超过了限制,直接取最后 limit 字节 + b.data = append(b.data[:0], p[n-b.limit:]...) + return + } + + available := b.limit - len(b.data) + if n <= available { + // 空间足够,直接追加 + b.data = append(b.data, p...) + } else { + // 空间不足,需要移除旧数据 + toRemove := n - available + b.data = append(b.data[toRemove:], p...) + } + return +} + +// Bytes 返回缓冲区内的所有数据 +func (b *TailBuffer) Bytes() []byte { + return b.data +} + +// String 返回缓冲区内的字符串表示 +func (b *TailBuffer) String() string { + return string(b.data) +} + +// Len 返回当前存储的数据长度 +func (b *TailBuffer) Len() int { + return len(b.data) +} + +// TrimLog 裁剪日志,保留末尾指定大小 +func TrimLog(content string, limit int) string { + if len(content) <= limit { + return content + } + // 简单裁剪,不考虑字符完整性,因为这是针对大文本的保护 + return fmt.Sprintf("\n\n[System] 日志过长,已自动截断,仅保留末尾 %d MB...\n\n", limit/1024/1024) + content[len(content)-limit:] +}