// Pacage logger provides a simple wrapper around zap logger package logger import ( "encoding/json" "fmt" "os" "strings" "time" "go.uber.org/zap" "go.uber.org/zap/zapcore" "gopkg.in/natefinch/lumberjack.v2" "github.com/runningwater/gohub/pkg/app" ) // Logger 全局日志对象 var Logger *zap.Logger // Init 初始化日志 func Init(filename string, maxSize, maxBackup, maxAge int, compress bool, logType, level string) { // 获取日志写入介质 writeSyncer := getLogWriter(filename, maxSize, maxBackup, maxAge, compress, logType) // 设置日志等级, 具体见 config/log.go logLevel := new(zap.AtomicLevel) if err := logLevel.UnmarshalText([]byte(level)); err != nil { fmt.Println("日志初始化错误,日志级别设置有误。请修改 config/log.go 中的 log.level") } // 初始化 core core := zapcore.NewCore(getEncoder(), writeSyncer, logLevel) // 初始化 logger Logger = zap.New(core, zap.AddCaller(), // 调用文件和行号,内部使用 runtime.Caller zap.AddCallerSkip(1), // 调用文件和行号,内部使用 runtime.Caller // zap.Development(), // 开发模式,堆栈跟踪 zap.AddStacktrace(zapcore.ErrorLevel), // 记录错误级别以上的堆栈信息 ) // 设置全局 logger // 将自定义的 logger 替换 zap 的全局 logger zap.ReplaceGlobals(Logger) } // getEncoder 获取编码器(如何写入日志), 输出: 日志格式 func getEncoder() zapcore.Encoder { // 日志模式规则 encoderConfig := zapcore.EncoderConfig{ TimeKey: "time", // 时间字段名 LevelKey: "level", // 日志级别字段名 NameKey: "logger", // 日志名称字段名 CallerKey: "caller", // 调用者字段名 FunctionKey: zapcore.OmitKey, // 函数名字段名,不显示 MessageKey: "msg", // 消息字段名 StacktraceKey: "stacktrace", // 堆栈跟踪字段名 LineEnding: zapcore.DefaultLineEnding, // 每行日志的结尾添加换行符 EncodeLevel: zapcore.CapitalLevelEncoder, // 大写编码器 EncodeTime: customTimeEncoder, // 自定义时间编码器 EncodeDuration: zapcore.SecondsDurationEncoder, // 以秒为单位显示持续时间 EncodeCaller: zapcore.ShortCallerEncoder, // 短路径编码器 } if app.IsLocal() { encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder // 彩色编码器 // 如果是本地环境,使用 console 编码器 return zapcore.NewConsoleEncoder(encoderConfig) } // 如果不是本地环境,使用 JSON 编码器 return zapcore.NewJSONEncoder(encoderConfig) } func customTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) { // 自定义时间格式 enc.AppendString(t.Format("2006-01-02 15:04:05")) } // getLogWriter 获取日志写入介质 // os.Stdout: 标准输出 // 文件输出 func getLogWriter(filename string, maxSize, maxBackup, maxAge int, compress bool, logType string) zapcore.WriteSyncer { // 如果配置了按日期记录日志,则使用按日期记录日志 if logType == "daily" { logName := time.Now().Format("2006-01-02") + ".log" filename = strings.Replace(filename, "logs.log", logName, 1) } // 滚动日志,详见 config/log.go lumberJackLogger := &lumberjack.Logger{ Filename: filename, // 日志文件名 MaxSize: maxSize, // 每个日志文件保存的最大尺寸 单位:MB MaxBackups: maxBackup, // 日志文件最多保存多少个备份 MaxAge: maxAge, // 文件最多保存多少天 Compress: compress, // 是否压缩 } if app.IsLocal() { // 如果是本地环境,使用标准输出和文件输出 return zapcore.NewMultiWriteSyncer( zapcore.AddSync(os.Stdout), zapcore.AddSync(lumberJackLogger), ) } else { // 生产环境只记录文件 return zapcore.AddSync(lumberJackLogger) } } // Dump 调试专用, 不会中断程序,会在终端打印 warning 信息。 // 第一个参数会使用 json.Marshal 进行渲染,第二个参数可选 // // logger.Dump(user.User{Name: "test"}) // logger.Dump(user.User{Name: "test"}, "用户信息") func Dump(v any, msg ...string) { valueStr := jsonString(v) if len(msg) > 0 { Logger.Warn("Dump", zap.String(msg[0], valueStr)) } else { Logger.Warn("Dump", zap.String("data", valueStr)) } } // LogIf 当 err != nill 时记录 error 等级的日志 func LogIf(err error) { if err != nil { Logger.Error("Error Occurred: ", zap.Error(err)) } } // LogWarnIf 当 err != nill 时记录 warning 等级的日志 func LogWarnIf(err error) { if err != nil { Logger.Warn("Error Occurred: ", zap.Error(err)) } } // LogInfoIf 当 err != nill 时记录 info 等级的日志 func LogInfoIf(err error) { if err != nil { Logger.Info("Error Occurred: ", zap.Error(err)) } } // Debug 调试专用, 不会中断程序 // // logger.Debug("Database", zap.String("sql", sql)) func Debug(msg string, fields ...zap.Field) { Logger.Debug(msg, fields...) } // Info 信息级别 func Info(msg string, fields ...zap.Field) { Logger.Info(msg, fields...) } // Warn 警告级别 func Warn(msg string, fields ...zap.Field) { Logger.Warn(msg, fields...) } // Error 错误级别 func Error(msg string, fields ...zap.Field) { Logger.Error(msg, fields...) } // Fatal 致命级别 func Fatal(msg string, fields ...zap.Field) { Logger.Fatal(msg, fields...) } // DebugString 记录一条字符串类型的 debug 日志 // // logger.DebugString("User", "name", "John") func DebugString(moduleName, name, msg string) { Logger.Debug(moduleName, zap.String(name, msg)) } func InfoString(moduleName, name, msg string) { Logger.Info(moduleName, zap.String(name, msg)) } func WarnString(moduleName, name, msg string) { Logger.Warn(moduleName, zap.String(name, msg)) } func ErrorString(moduleName, name, msg string) { Logger.Error(moduleName, zap.String(name, msg)) } func FatalString(moduleName, name, msg string) { Logger.Fatal(moduleName, zap.String(name, msg)) } // DebugJSON 记录对象类型的 debug 日志,使用 json.Marshal 进行编码。调用示例: // // logger.DebugJSON("Auth", "读取登录用户", auth.CurrentUser()) func DebugJSON(moduleName, name string, value any) { Logger.Debug(moduleName, zap.String(name, jsonString(value))) } func InfoJSON(moduleName, name string, value any) { Logger.Info(moduleName, zap.String(name, jsonString(value))) } func WarnJSON(moduleName, name string, value any) { Logger.Warn(moduleName, zap.String(name, jsonString(value))) } func ErrorJSON(moduleName, name string, value any) { Logger.Error(moduleName, zap.String(name, jsonString(value))) } func FatalJSON(moduleName, name string, value any) { Logger.Fatal(moduleName, zap.String(name, jsonString(value))) } func jsonString(v any) string { b, err := json.Marshal(v) if err != nil { Logger.Error("Logger", zap.String("json.Marshal error", err.Error())) } return string(b) }