171 lines
4.1 KiB
Go
171 lines
4.1 KiB
Go
|
package logger
|
|||
|
|
|||
|
import (
|
|||
|
"fmt"
|
|||
|
"log"
|
|||
|
"os"
|
|||
|
"path/filepath"
|
|||
|
"strings"
|
|||
|
"sync"
|
|||
|
"time"
|
|||
|
|
|||
|
"github.com/gin-gonic/gin"
|
|||
|
"github.com/google/uuid"
|
|||
|
rotatelogs "github.com/lestrrat-go/file-rotatelogs"
|
|||
|
"go.uber.org/zap"
|
|||
|
"go.uber.org/zap/zapcore"
|
|||
|
)
|
|||
|
|
|||
|
const (
|
|||
|
TRACKED = "trackID"
|
|||
|
)
|
|||
|
|
|||
|
var (
|
|||
|
Logger *zap.Logger
|
|||
|
loggerMux sync.Once
|
|||
|
CallerSkip = 2
|
|||
|
initErr error
|
|||
|
)
|
|||
|
|
|||
|
// BuildLogger 初始化日志系统
|
|||
|
func BuildLogger() error {
|
|||
|
loggerMux.Do(func() {
|
|||
|
initErr = initLogger()
|
|||
|
})
|
|||
|
return initErr
|
|||
|
}
|
|||
|
|
|||
|
// initLogger 实际的日志初始化逻辑
|
|||
|
func initLogger() error {
|
|||
|
logLevel := getLogLevel()
|
|||
|
logPath := os.Getenv("LOG_PATH")
|
|||
|
if logPath == "" {
|
|||
|
logPath = "./logs" // 默认日志路径
|
|||
|
}
|
|||
|
|
|||
|
// 尝试创建日志目录
|
|||
|
if err := os.MkdirAll(logPath, 0755); err != nil {
|
|||
|
// 使用标准库的log记录错误,因为zap还没初始化
|
|||
|
log.Printf("无法创建日志目录 %s: %v", logPath, err)
|
|||
|
return fmt.Errorf("无法创建日志目录 %s: %w", logPath, err)
|
|||
|
}
|
|||
|
|
|||
|
// 配置滚动日志(按天滚动,保留28天)
|
|||
|
logFileName := filepath.Join(logPath, "ego.%Y%m%d.log")
|
|||
|
logs, err := rotatelogs.New(
|
|||
|
logFileName,
|
|||
|
rotatelogs.WithMaxAge(28*24*time.Hour),
|
|||
|
rotatelogs.WithRotationTime(24*time.Hour),
|
|||
|
)
|
|||
|
if err != nil {
|
|||
|
log.Printf("创建滚动日志失败: %v", err)
|
|||
|
return fmt.Errorf("创建滚动日志失败: %w", err)
|
|||
|
}
|
|||
|
|
|||
|
// 配置控制台输出
|
|||
|
consoleEncoder := zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig())
|
|||
|
consoleSyncer := zapcore.AddSync(os.Stdout)
|
|||
|
|
|||
|
// 配置文件输出(生产环境JSON格式)
|
|||
|
prodEncoderConfig := zap.NewProductionEncoderConfig()
|
|||
|
prodEncoderConfig.EncodeTime = TimeEncoder
|
|||
|
prodEncoderConfig.CallerKey = "caller"
|
|||
|
prodEncoderConfig.EncodeCaller = zapcore.ShortCallerEncoder
|
|||
|
prodEncoder := zapcore.NewJSONEncoder(prodEncoderConfig)
|
|||
|
fileSyncer := zapcore.AddSync(logs)
|
|||
|
|
|||
|
// 创建日志核心
|
|||
|
core := zapcore.NewTee(
|
|||
|
zapcore.NewCore(consoleEncoder, consoleSyncer, logLevel),
|
|||
|
zapcore.NewCore(prodEncoder, fileSyncer, logLevel),
|
|||
|
)
|
|||
|
|
|||
|
// 初始化 Logger
|
|||
|
Logger = zap.New(core, zap.AddCaller(), zap.AddCallerSkip(CallerSkip))
|
|||
|
|
|||
|
// 记录初始化成功
|
|||
|
Logger.Info("日志系统初始化成功",
|
|||
|
zap.String("log_path", logPath),
|
|||
|
zap.String("log_level", logLevel.String()),
|
|||
|
)
|
|||
|
|
|||
|
return nil
|
|||
|
}
|
|||
|
|
|||
|
// Debug 记录调试日志
|
|||
|
func Debug(c *gin.Context, msg string, fields ...zap.Field) {
|
|||
|
if Logger != nil {
|
|||
|
logWithTrack(c, zap.DebugLevel, msg, fields...)
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Info 记录信息日志
|
|||
|
func Info(c *gin.Context, msg string, fields ...zap.Field) {
|
|||
|
if Logger != nil {
|
|||
|
logWithTrack(c, zap.InfoLevel, msg, fields...)
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Warn 记录警告日志
|
|||
|
func Warn(c *gin.Context, msg string, fields ...zap.Field) {
|
|||
|
if Logger != nil {
|
|||
|
logWithTrack(c, zap.WarnLevel, msg, fields...)
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Error 记录错误日志
|
|||
|
func Error(c *gin.Context, msg string, fields ...zap.Field) {
|
|||
|
if Logger != nil {
|
|||
|
logWithTrack(c, zap.ErrorLevel, msg, fields...)
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// logWithTrack 封装带 trackID 的日志记录
|
|||
|
func logWithTrack(c *gin.Context, level zapcore.Level, msg string, fields ...zap.Field) {
|
|||
|
if Logger == nil {
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
if c == nil {
|
|||
|
Logger.Check(level, msg).Write(fields...)
|
|||
|
return
|
|||
|
}
|
|||
|
trackID := GetTrackID(c)
|
|||
|
Logger.Check(level, msg).Write(append(fields, zap.String("trackID", trackID))...)
|
|||
|
}
|
|||
|
|
|||
|
// GetTrackID 获取或生成 trackID
|
|||
|
func GetTrackID(c *gin.Context) string {
|
|||
|
if trackID, exists := c.Get(TRACKED); exists {
|
|||
|
return trackID.(string)
|
|||
|
}
|
|||
|
if random, err := uuid.NewRandom(); err == nil {
|
|||
|
// 生成新的 trackID 并存入上下文
|
|||
|
newTrackID := strings.ToLower(random.String())
|
|||
|
c.Set(TRACKED, newTrackID)
|
|||
|
return newTrackID
|
|||
|
}
|
|||
|
return ""
|
|||
|
}
|
|||
|
|
|||
|
// getLogLevel 根据环境变量获取日志级别
|
|||
|
func getLogLevel() zapcore.Level {
|
|||
|
switch strings.ToLower(os.Getenv("LOG_LEVEL")) {
|
|||
|
case "debug":
|
|||
|
return zap.DebugLevel
|
|||
|
case "info":
|
|||
|
return zap.InfoLevel
|
|||
|
case "warn":
|
|||
|
return zap.WarnLevel
|
|||
|
case "error":
|
|||
|
return zap.ErrorLevel
|
|||
|
default:
|
|||
|
return zap.InfoLevel // 默认使用 Info 级别
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// TimeEncoder 自定义时间格式
|
|||
|
func TimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
|
|||
|
enc.AppendString(t.Format("2006-01-02 15:04:05.999"))
|
|||
|
}
|