[feature] migrate to monorepo
Some checks failed
Build Backend / Build Docker Image (push) Successful in 3m33s
Test Backend / test (push) Failing after 31s

This commit is contained in:
CDN 2025-02-21 00:49:20 +08:00
commit 05ddc1f783
Signed by: CDN
GPG key ID: 0C656827F9F80080
267 changed files with 75165 additions and 0 deletions

View file

@ -0,0 +1,192 @@
package middleware
import (
"fmt"
"os"
"path/filepath"
"time"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/rs/zerolog"
"gopkg.in/natefinch/lumberjack.v2"
"tss-rocks-be/internal/types"
)
// AccessLogConfig 访问日志配置
type AccessLogConfig struct {
// 是否启用控制台输出
EnableConsole bool `yaml:"enable_console"`
// 是否启用文件日志
EnableFile bool `yaml:"enable_file"`
// 日志文件路径
FilePath string `yaml:"file_path"`
// 日志格式 (json 或 text)
Format string `yaml:"format"`
// 日志级别
Level string `yaml:"level"`
// 日志轮转配置
Rotation struct {
MaxSize int `yaml:"max_size"` // 每个日志文件的最大大小MB
MaxAge int `yaml:"max_age"` // 保留旧日志文件的最大天数
MaxBackups int `yaml:"max_backups"` // 保留的旧日志文件的最大数量
Compress bool `yaml:"compress"` // 是否压缩旧日志文件
LocalTime bool `yaml:"local_time"` // 使用本地时间作为轮转时间
} `yaml:"rotation"`
}
// accessLogger 访问日志记录器
type accessLogger struct {
consoleLogger *zerolog.Logger
fileLogger *zerolog.Logger
logWriter *lumberjack.Logger
config *types.AccessLogConfig
}
// Close 关闭日志文件
func (l *accessLogger) Close() error {
if l.logWriter != nil {
return l.logWriter.Close()
}
return nil
}
// newAccessLogger 创建新的访问日志记录器
func newAccessLogger(config *types.AccessLogConfig) (*accessLogger, error) {
var consoleLogger, fileLogger *zerolog.Logger
var logWriter *lumberjack.Logger
// 设置日志级别
level, err := zerolog.ParseLevel(config.Level)
if err != nil {
level = zerolog.InfoLevel
}
zerolog.SetGlobalLevel(level)
// 配置控制台日志
if config.EnableConsole {
logger := zerolog.New(os.Stdout).
With().
Timestamp().
Logger()
if config.Format == "text" {
logger = logger.Output(zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC3339})
}
consoleLogger = &logger
}
// 配置文件日志
if config.EnableFile {
// 确保日志目录存在
if err := os.MkdirAll(filepath.Dir(config.FilePath), 0755); err != nil {
return nil, fmt.Errorf("failed to create log directory: %w", err)
}
// 配置日志轮转
logWriter = &lumberjack.Logger{
Filename: config.FilePath,
MaxSize: config.Rotation.MaxSize, // MB
MaxAge: config.Rotation.MaxAge, // days
MaxBackups: config.Rotation.MaxBackups, // files
Compress: config.Rotation.Compress, // 是否压缩
LocalTime: config.Rotation.LocalTime, // 使用本地时间
}
logger := zerolog.New(logWriter).
With().
Timestamp().
Logger()
fileLogger = &logger
}
return &accessLogger{
consoleLogger: consoleLogger,
fileLogger: fileLogger,
logWriter: logWriter,
config: config,
}, nil
}
// logEvent 记录日志事件
func (l *accessLogger) logEvent(fields map[string]interface{}, msg string) {
if l.consoleLogger != nil {
event := l.consoleLogger.Info()
for k, v := range fields {
event = event.Interface(k, v)
}
event.Msg(msg)
}
if l.fileLogger != nil {
event := l.fileLogger.Info()
for k, v := range fields {
event = event.Interface(k, v)
}
event.Msg(msg)
}
}
// AccessLog 创建访问日志中间件
func AccessLog(config *types.AccessLogConfig) (gin.HandlerFunc, error) {
logger, err := newAccessLogger(config)
if err != nil {
return nil, err
}
return func(c *gin.Context) {
// 用于测试时关闭日志文件
if c == nil {
if err := logger.Close(); err != nil {
fmt.Printf("Error closing log file: %v\n", err)
}
return
}
start := time.Now()
requestID := uuid.New().String()
path := c.Request.URL.Path
query := c.Request.URL.RawQuery
// 设置请求ID到上下文
c.Set("request_id", requestID)
// 处理请求
c.Next()
// 计算处理时间
latency := time.Since(start)
// 获取用户ID如果已认证
var userID interface{}
if id, exists := c.Get("user_id"); exists {
userID = id
}
// 准备日志字段
fields := map[string]interface{}{
"request_id": requestID,
"method": c.Request.Method,
"path": path,
"query": query,
"ip": c.ClientIP(),
"user_agent": c.Request.UserAgent(),
"status": c.Writer.Status(),
"size": c.Writer.Size(),
"latency_ms": latency.Milliseconds(),
"component": "access_log",
}
if userID != nil {
fields["user_id"] = userID
}
// 如果有错误,添加到日志中
if len(c.Errors) > 0 {
fields["error"] = c.Errors.String()
}
// 记录日志
logger.logEvent(fields, fmt.Sprintf("%s %s", c.Request.Method, path))
}, nil
}