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"))
|
||
}
|