|
@@ -2,24 +2,66 @@ package log
|
|
|
|
|
|
import (
|
|
|
"context"
|
|
|
+ "fmt"
|
|
|
"github.com/gin-gonic/gin"
|
|
|
"github.com/spf13/viper"
|
|
|
"go.uber.org/zap"
|
|
|
"go.uber.org/zap/zapcore"
|
|
|
"gopkg.in/natefinch/lumberjack.v2"
|
|
|
"os"
|
|
|
+ "path/filepath"
|
|
|
"time"
|
|
|
)
|
|
|
|
|
|
const ctxLoggerKey = "zapLogger"
|
|
|
|
|
|
+// ServiceType 服务类型
|
|
|
+type ServiceType int
|
|
|
+
|
|
|
+const (
|
|
|
+ API ServiceType = iota // API服务
|
|
|
+ Task // Task服务
|
|
|
+)
|
|
|
+
|
|
|
type Logger struct {
|
|
|
*zap.Logger
|
|
|
}
|
|
|
|
|
|
+// NewLog 创建一个新的日志实例,兼容旧版本配置
|
|
|
func NewLog(conf *viper.Viper) *Logger {
|
|
|
- // log address "out.log" User-defined
|
|
|
- lp := conf.GetString("log.log_file_name")
|
|
|
+ return NewServiceLog(conf, API)
|
|
|
+}
|
|
|
+
|
|
|
+// NewServiceLog 创建特定服务类型的日志实例
|
|
|
+func NewServiceLog(conf *viper.Viper, serviceType ServiceType) *Logger {
|
|
|
+ var logPath string
|
|
|
+
|
|
|
+ // 根据服务类型和日期生成日志文件名
|
|
|
+ logFormat := conf.GetString("log.log_format") // 日期格式,如"2006-01-02"
|
|
|
+ currentDate := time.Now().Format(logFormat)
|
|
|
+
|
|
|
+ if serviceType == API && conf.IsSet("log.api_log_file") {
|
|
|
+ // 使用API日志路径
|
|
|
+ logPath = conf.GetString("log.api_log_file")
|
|
|
+ logPath = fmt.Sprintf(logPath, currentDate) // 替换日期占位符
|
|
|
+ } else if serviceType == Task && conf.IsSet("log.task_log_file") {
|
|
|
+ // 使用Task日志路径
|
|
|
+ logPath = conf.GetString("log.task_log_file")
|
|
|
+ logPath = fmt.Sprintf(logPath, currentDate) // 替换日期占位符
|
|
|
+ } else {
|
|
|
+ // 如果未配置专用日志路径,则使用旧配置
|
|
|
+ logPath = conf.GetString("log.log_file_name")
|
|
|
+ }
|
|
|
+
|
|
|
+ // 确保日志目录存在
|
|
|
+ if err := ensureDirExists(logPath); err != nil {
|
|
|
+ // 如果创建目录失败,回退到临时目录
|
|
|
+ fmt.Printf("Error creating log directory: %v, using default path\n", err)
|
|
|
+ logPath = "./logs/app.log"
|
|
|
+ ensureDirExists(logPath)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取日志级别
|
|
|
lv := conf.GetString("log.log_level")
|
|
|
var level zapcore.Level
|
|
|
//debug<info<warn<error<fatal<panic
|
|
@@ -35,14 +77,17 @@ func NewLog(conf *viper.Viper) *Logger {
|
|
|
default:
|
|
|
level = zap.InfoLevel
|
|
|
}
|
|
|
+
|
|
|
+ // 日志轮转配置
|
|
|
hook := lumberjack.Logger{
|
|
|
- Filename: lp, // Log file path
|
|
|
- MaxSize: conf.GetInt("log.max_size"), // Maximum size unit for each log file: M
|
|
|
- MaxBackups: conf.GetInt("log.max_backups"), // The maximum number of backups that can be saved for log files
|
|
|
- MaxAge: conf.GetInt("log.max_age"), // Maximum number of days the file can be saved
|
|
|
- Compress: conf.GetBool("log.compress"), // Compression or not
|
|
|
+ Filename: logPath, // 日志文件路径
|
|
|
+ MaxSize: conf.GetInt("log.max_size"), // 每个日志文件的最大大小单位:M
|
|
|
+ MaxBackups: conf.GetInt("log.max_backups"), // 日志文件最多保存的备份数
|
|
|
+ MaxAge: conf.GetInt("log.max_age"), // 文件最多保存的天数
|
|
|
+ Compress: conf.GetBool("log.compress"), // 是否压缩
|
|
|
}
|
|
|
|
|
|
+ // 配置日志编码器
|
|
|
var encoder zapcore.Encoder
|
|
|
if conf.GetString("log.encoding") == "console" {
|
|
|
encoder = zapcore.NewConsoleEncoder(zapcore.EncoderConfig{
|
|
@@ -60,7 +105,7 @@ func NewLog(conf *viper.Viper) *Logger {
|
|
|
})
|
|
|
} else {
|
|
|
encoder = zapcore.NewJSONEncoder(zapcore.EncoderConfig{
|
|
|
- TimeKey: "ts",
|
|
|
+ TimeKey: "time",
|
|
|
LevelKey: "level",
|
|
|
NameKey: "logger",
|
|
|
CallerKey: "caller",
|
|
@@ -69,24 +114,61 @@ func NewLog(conf *viper.Viper) *Logger {
|
|
|
StacktraceKey: "stacktrace",
|
|
|
LineEnding: zapcore.DefaultLineEnding,
|
|
|
EncodeLevel: zapcore.LowercaseLevelEncoder,
|
|
|
- EncodeTime: zapcore.EpochTimeEncoder,
|
|
|
- EncodeDuration: zapcore.SecondsDurationEncoder,
|
|
|
+ EncodeTime: timeEncoder, // 使用自定义时间格式
|
|
|
+ EncodeDuration: zapcore.StringDurationEncoder,
|
|
|
EncodeCaller: zapcore.ShortCallerEncoder,
|
|
|
})
|
|
|
}
|
|
|
- core := zapcore.NewCore(
|
|
|
- encoder,
|
|
|
- zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout), zapcore.AddSync(&hook)), // Print to console and file
|
|
|
- level,
|
|
|
+
|
|
|
+ // 创建日志核心
|
|
|
+ logOutput := zapcore.NewMultiWriteSyncer(
|
|
|
+ zapcore.AddSync(os.Stdout), // 同时输出到控制台
|
|
|
+ zapcore.AddSync(&hook), // 和文件
|
|
|
)
|
|
|
+ core := zapcore.NewCore(encoder, logOutput, level)
|
|
|
+
|
|
|
+ // 开发环境与生产环境的差异化配置
|
|
|
+ var logger *zap.Logger
|
|
|
if conf.GetString("env") != "prod" {
|
|
|
- return &Logger{zap.New(core, zap.Development(), zap.AddCaller(), zap.AddStacktrace(zap.ErrorLevel))}
|
|
|
+ logger = zap.New(core,
|
|
|
+ zap.Development(),
|
|
|
+ zap.AddCaller(),
|
|
|
+ zap.AddStacktrace(zap.ErrorLevel),
|
|
|
+ )
|
|
|
+ } else {
|
|
|
+ logger = zap.New(core,
|
|
|
+ zap.AddCaller(),
|
|
|
+ zap.AddStacktrace(zap.ErrorLevel),
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ // 添加服务类型标识
|
|
|
+ serviceTypeField := zap.String("service", serviceTypeToString(serviceType))
|
|
|
+ logger = logger.With(serviceTypeField)
|
|
|
+
|
|
|
+ return &Logger{logger}
|
|
|
+}
|
|
|
+
|
|
|
+// ensureDirExists 确保日志目录存在
|
|
|
+func ensureDirExists(filePath string) error {
|
|
|
+ dir := filepath.Dir(filePath)
|
|
|
+ return os.MkdirAll(dir, 0755)
|
|
|
+}
|
|
|
+
|
|
|
+// serviceTypeToString 将服务类型转换为字符串
|
|
|
+func serviceTypeToString(serviceType ServiceType) string {
|
|
|
+ switch serviceType {
|
|
|
+ case API:
|
|
|
+ return "api"
|
|
|
+ case Task:
|
|
|
+ return "task"
|
|
|
+ default:
|
|
|
+ return "unknown"
|
|
|
}
|
|
|
- return &Logger{zap.New(core, zap.AddCaller(), zap.AddStacktrace(zap.ErrorLevel))}
|
|
|
}
|
|
|
|
|
|
+// timeEncoder 自定义时间编码器,提供高精度时间戳
|
|
|
func timeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
|
|
|
- //enc.AppendString(t.Format("2006-01-02 15:04:05"))
|
|
|
enc.AppendString(t.Format("2006-01-02 15:04:05.000000000"))
|
|
|
}
|
|
|
|