package filesystem import ( "encoding/json" "fmt" "os" "path/filepath" "strings" "sync" "time" ) // AuditOperation 审计操作类型 type AuditOperation string const ( OperationRead AuditOperation = "read" // 读取文件 OperationWrite AuditOperation = "write" // 写入文件 OperationDelete AuditOperation = "delete" // 删除文件 OperationCreate AuditOperation = "create" // 创建目录 OperationRename AuditOperation = "rename" // 重命名 OperationCopy AuditOperation = "copy" // 复制 OperationMove AuditOperation = "move" // 移动 OperationList AuditOperation = "list" // 列出目录 OperationDownload AuditOperation = "download" // 下载 ) // AuditLogEntry 审计日志条目 type AuditLogEntry struct { Timestamp time.Time `json:"timestamp"` // 操作时间 Operation AuditOperation `json:"operation"` // 操作类型 Path string `json:"path"` // 文件路径 OldPath string `json:"old_path,omitempty"` // 原路径(重命名/移动) Size int64 `json:"size,omitempty"` // 文件大小 IsDirectory bool `json:"is_directory"` // 是否为目录 Success bool `json:"success"` // 操作是否成功 Error string `json:"error,omitempty"` // 错误信息 UserAgent string `json:"user_agent,omitempty"` // 用户代理 IPAddress string `json:"ip_address,omitempty"` // IP地址 } // AuditLogger 审计日志记录器 type AuditLogger struct { logFile *os.File logDir string mu sync.Mutex buffer []AuditLogEntry bufferSize int stopChan chan struct{} initialized bool // 懒初始化标志(失败时允许重试) } // NewAuditLogger 创建审计日志记录器(延迟初始化,不立即打开文件) func NewAuditLogger(logDir string) (*AuditLogger, error) { return &AuditLogger{ logDir: logDir, buffer: make([]AuditLogEntry, 0, 100), bufferSize: 100, stopChan: make(chan struct{}), }, nil } // ensureReady 首次写入时才创建目录、打开文件、启动后台协程 // 在 a.mu.Lock 保护下调用,失败时下次 Log 会重试 func (a *AuditLogger) ensureReady() error { if a.initialized { return nil } if err := os.MkdirAll(a.logDir, 0755); err != nil { return fmt.Errorf("创建日志目录失败: %v", err) } timestamp := time.Now().Format("2006-01-02") f, err := os.OpenFile( filepath.Join(a.logDir, fmt.Sprintf("audit_%s.log", timestamp)), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) if err != nil { return err } a.logFile = f a.initialized = true go a.backgroundFlush() return nil } // Log 记录操作日志 func (a *AuditLogger) Log(entry AuditLogEntry) error { // 设置时间戳 if entry.Timestamp.IsZero() { entry.Timestamp = time.Now() } a.mu.Lock() defer a.mu.Unlock() // 懒初始化:首次写入时才打开文件 if a.logFile == nil { if err := a.ensureReady(); err != nil { return err } } // 添加到缓冲区 a.buffer = append(a.buffer, entry) // 如果缓冲区满了,立即写入 if len(a.buffer) >= a.bufferSize { if err := a.flush(); err != nil { return err } } return nil } // LogDelete 记录删除操作(便捷方法) func (a *AuditLogger) LogDelete(path string, isDir bool, size int64, err error) { entry := AuditLogEntry{ Timestamp: time.Now(), Operation: OperationDelete, Path: path, Size: size, IsDirectory: isDir, Success: err == nil, } if err != nil { entry.Error = err.Error() } _ = a.Log(entry) } // LogWrite 记录写入操作(便捷方法) func (a *AuditLogger) LogWrite(path string, size int64, err error) { entry := AuditLogEntry{ Timestamp: time.Now(), Operation: OperationWrite, Path: path, Size: size, IsDirectory: false, Success: err == nil, } if err != nil { entry.Error = err.Error() } _ = a.Log(entry) } // LogRead 记录读取操作(便捷方法) func (a *AuditLogger) LogRead(path string, size int64, err error) { entry := AuditLogEntry{ Timestamp: time.Now(), Operation: OperationRead, Path: path, Size: size, IsDirectory: false, Success: err == nil, } if err != nil { entry.Error = err.Error() } _ = a.Log(entry) } // flush 将缓冲区写入文件 func (a *AuditLogger) flush() error { if len(a.buffer) == 0 { return nil } // 序列化所有条目为JSON(每行一个) for _, entry := range a.buffer { data, err := json.Marshal(entry) if err != nil { continue // 序列化失败,跳过该条目 } if _, err := a.logFile.Write(append(data, '\n')); err != nil { return err } } // 刷新到磁盘 if err := a.logFile.Sync(); err != nil { return err } // 清空缓冲区 a.buffer = a.buffer[:0] return nil } // backgroundFlush 后台协程,定期刷新缓冲区 func (a *AuditLogger) backgroundFlush() { ticker := time.NewTicker(5 * time.Second) // 每5秒刷新一次 defer ticker.Stop() for { select { case <-ticker.C: a.mu.Lock() _ = a.flush() a.mu.Unlock() case <-a.stopChan: // 停止前刷新一次 a.mu.Lock() _ = a.flush() a.mu.Unlock() return } } } // Close 关闭审计日志记录器 func (a *AuditLogger) Close() error { close(a.stopChan) a.mu.Lock() defer a.mu.Unlock() // 从未写入过日志,无需关闭 if a.logFile == nil { return nil } // 刷新剩余缓冲区 if err := a.flush(); err != nil { return err } // 关闭文件 return a.logFile.Close() } // GetRecentLogs 获取最近的审计日志 func GetRecentLogs(logDir string, limit int) ([]AuditLogEntry, error) { // 读取今天的日志文件 timestamp := time.Now().Format("2006-01-02") logPath := filepath.Join(logDir, fmt.Sprintf("audit_%s.log", timestamp)) data, err := os.ReadFile(logPath) if err != nil { return nil, err } // 解析JSON(每行一个条目) var entries []AuditLogEntry lines := parseLines(string(data)) // 从后往前读取(最新的在前) start := len(lines) - limit if start < 0 { start = 0 } for i := len(lines) - 1; i >= start; i-- { var entry AuditLogEntry if err := json.Unmarshal([]byte(lines[i]), &entry); err == nil { entries = append(entries, entry) } } return entries, nil } // parseLines 解析文本为行(过滤空行) func parseLines(text string) []string { raw := strings.Split(text, "\n") lines := make([]string, 0, len(raw)) for _, line := range raw { if line != "" { lines = append(lines, line) } } return lines }