重构:文件系统模块化架构,优化应用启动流程
This commit is contained in:
330
internal/filesystem/audit_log.go
Normal file
330
internal/filesystem/audit_log.go
Normal file
@@ -0,0 +1,330 @@
|
||||
package filesystem
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// AuditOperation 审计操作类型
|
||||
type AuditOperation string
|
||||
|
||||
const (
|
||||
OperationRead AuditOperation = "read" // 读取文件
|
||||
OperationWrite AuditOperation = "write" // 写入文件
|
||||
OperationDelete AuditOperation = "delete" // 删除文件
|
||||
OperationCreate AuditOperation = "create" // 创建目录
|
||||
OperationRename AuditOperation = "rename" // 重命名
|
||||
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
|
||||
logPath string
|
||||
mu sync.Mutex
|
||||
buffer []AuditLogEntry
|
||||
bufferSize int
|
||||
stopChan chan struct{}
|
||||
}
|
||||
|
||||
// NewAuditLogger 创建审计日志记录器
|
||||
func NewAuditLogger(logDir string) (*AuditLogger, error) {
|
||||
// 创建日志目录
|
||||
if err := os.MkdirAll(logDir, 0755); err != nil {
|
||||
return nil, fmt.Errorf("创建日志目录失败: %v", err)
|
||||
}
|
||||
|
||||
// 生成日志文件名(按日期)
|
||||
timestamp := time.Now().Format("2006-01-02")
|
||||
logPath := filepath.Join(logDir, fmt.Sprintf("audit_%s.log", timestamp))
|
||||
|
||||
// 打开日志文件(追加模式)
|
||||
logFile, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("打开日志文件失败: %v", err)
|
||||
}
|
||||
|
||||
logger := &AuditLogger{
|
||||
logFile: logFile,
|
||||
logPath: logPath,
|
||||
buffer: make([]AuditLogEntry, 0, 100),
|
||||
bufferSize: 100, // 缓冲100条记录后批量写入
|
||||
stopChan: make(chan struct{}),
|
||||
}
|
||||
|
||||
// 启动后台协程,定期刷新缓冲区
|
||||
go logger.backgroundFlush()
|
||||
|
||||
return logger, nil
|
||||
}
|
||||
|
||||
// Log 记录操作日志
|
||||
func (a *AuditLogger) Log(entry AuditLogEntry) error {
|
||||
// 设置时间戳
|
||||
if entry.Timestamp.IsZero() {
|
||||
entry.Timestamp = time.Now()
|
||||
}
|
||||
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
|
||||
// 添加到缓冲区
|
||||
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 err := a.flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 关闭文件
|
||||
return a.logFile.Close()
|
||||
}
|
||||
|
||||
// RotateLog 日志轮转(每天创建新文件)
|
||||
func (a *AuditLogger) RotateLog() error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
|
||||
// 刷新缓冲区
|
||||
if err := a.flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 关闭当前文件
|
||||
if err := a.logFile.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 生成新的日志文件名
|
||||
timestamp := time.Now().Format("2006-01-02")
|
||||
logPath := filepath.Join(filepath.Dir(a.logPath), fmt.Sprintf("audit_%s.log", timestamp))
|
||||
|
||||
// 打开新文件
|
||||
logFile, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
a.logFile = logFile
|
||||
a.logPath = logPath
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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 {
|
||||
lines := make([]string, 0)
|
||||
current := ""
|
||||
|
||||
for _, ch := range text {
|
||||
if ch == '\n' {
|
||||
if current != "" {
|
||||
lines = append(lines, current)
|
||||
current = ""
|
||||
}
|
||||
} else {
|
||||
current += string(ch)
|
||||
}
|
||||
}
|
||||
|
||||
if current != "" {
|
||||
lines = append(lines, current)
|
||||
}
|
||||
|
||||
return lines
|
||||
}
|
||||
|
||||
// 全局审计日志记录器
|
||||
var globalAuditLogger *AuditLogger
|
||||
var auditLoggerOnce sync.Once
|
||||
|
||||
// InitAuditLogger 初始化全局审计日志记录器
|
||||
func InitAuditLogger(logDir string) error {
|
||||
var err error
|
||||
globalAuditLogger, err = NewAuditLogger(logDir)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetAuditLogger 获取全局审计日志记录器
|
||||
func GetAuditLogger() *AuditLogger {
|
||||
return globalAuditLogger
|
||||
}
|
||||
|
||||
// CloseAuditLogger 关闭全局审计日志记录器
|
||||
func CloseAuditLogger() error {
|
||||
if globalAuditLogger != nil {
|
||||
return globalAuditLogger.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user