optimize
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"maps"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// 审计日志 (JSON Lines, 一行一条事件, 落盘到 *auditDir/YYYY-MM-DD.log, 跨日自动轮转)
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
var (
|
||||
auditMu sync.Mutex
|
||||
auditFile *os.File
|
||||
auditCurDay string // 当前打开文件对应的日期 (YYYY-MM-DD)
|
||||
)
|
||||
|
||||
// initAudit 创建目录, 打开今天的日志文件
|
||||
func initAudit() error {
|
||||
if *auditDir == "" {
|
||||
log.Println("[audit] disabled (audit dir is empty)")
|
||||
return nil
|
||||
}
|
||||
if err := os.MkdirAll(*auditDir, 0o755); err != nil {
|
||||
return fmt.Errorf("create audit dir: %w", err)
|
||||
}
|
||||
if err := openAuditFor(time.Now()); err != nil {
|
||||
return err
|
||||
}
|
||||
log.Printf("[audit] writing to %s/%s.log", *auditDir, auditCurDay)
|
||||
return nil
|
||||
}
|
||||
|
||||
// openAuditFor 为指定时间打开对应日期的日志文件. 调用方需持有 auditMu.
|
||||
func openAuditFor(t time.Time) error {
|
||||
day := t.Format("2006-01-02")
|
||||
if day == auditCurDay && auditFile != nil {
|
||||
return nil
|
||||
}
|
||||
if auditFile != nil {
|
||||
_ = auditFile.Close()
|
||||
}
|
||||
path := filepath.Join(*auditDir, day+".log")
|
||||
f, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o644)
|
||||
if err != nil {
|
||||
return fmt.Errorf("open %s: %w", path, err)
|
||||
}
|
||||
auditFile = f
|
||||
auditCurDay = day
|
||||
return nil
|
||||
}
|
||||
|
||||
// audit 写一条审计事件. fields 里的键会合并到事件 JSON 中.
|
||||
// 写入失败仅记录到 stdout, 不会影响主流程.
|
||||
func audit(c *gin.Context, action string, fields map[string]any) {
|
||||
if *auditDir == "" {
|
||||
return
|
||||
}
|
||||
entry := map[string]any{
|
||||
"ts": time.Now().Format(time.RFC3339),
|
||||
"action": action,
|
||||
"ip": c.ClientIP(),
|
||||
"method": c.Request.Method,
|
||||
"path": c.Request.URL.Path,
|
||||
"ua": c.Request.UserAgent(),
|
||||
"status": c.Writer.Status(),
|
||||
"latency": time.Since(c.GetTime("t0")).String(),
|
||||
}
|
||||
maps.Copy(entry, fields)
|
||||
b, err := json.Marshal(entry)
|
||||
if err != nil {
|
||||
log.Printf("[audit] marshal err: %v", err)
|
||||
return
|
||||
}
|
||||
b = append(b, '\n')
|
||||
|
||||
auditMu.Lock()
|
||||
defer auditMu.Unlock()
|
||||
if err := openAuditFor(time.Now()); err != nil {
|
||||
log.Printf("[audit] open err: %v", err)
|
||||
return
|
||||
}
|
||||
if _, err := auditFile.Write(b); err != nil {
|
||||
log.Printf("[audit] write err: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// auditT0 记录请求进入时间, 供 audit() 计算 latency
|
||||
func auditT0() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
c.Set("t0", time.Now())
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user